From 4e780ad71329b0f54d6e76dce80a066b9c223eb3 Mon Sep 17 00:00:00 2001 From: "Enrico Fraccaroli (Galfurian)" Date: Fri, 27 Feb 2026 15:46:28 +0100 Subject: [PATCH 01/22] fix(keyboard): implement proper blocking logic and clarify fgets behavior --- kernel/inc/drivers/keyboard/keyboard.h | 7 +++ kernel/inc/process/wait.h | 7 +++ kernel/src/drivers/keyboard/keyboard.c | 28 +++++++++++- kernel/src/io/proc_video.c | 6 ++- kernel/src/process/wait.c | 34 ++++++++++++++ lib/inc/stdio.h | 13 ++++++ lib/src/stdio.c | 61 +++++++++++++++++++------- userspace/bin/login.c | 8 +++- 8 files changed, 144 insertions(+), 20 deletions(-) diff --git a/kernel/inc/drivers/keyboard/keyboard.h b/kernel/inc/drivers/keyboard/keyboard.h index fc6ae4b4..d9de3541 100644 --- a/kernel/inc/drivers/keyboard/keyboard.h +++ b/kernel/inc/drivers/keyboard/keyboard.h @@ -44,6 +44,13 @@ int keyboard_peek_front(void); /// @return 0 on success, 1 on error. int keyboard_initialize(void); +/// @brief Sleep until a keypress is available on the global keyboard queue. +/// +/// This is a simple helper used by higher‑level input code to block the +/// current process until a character arrives. It is safe to call from +/// kernel contexts that know they have the keyboard lock. +void keyboard_wait(void); + /// @brief De-initializes the keyboard drivers. /// @return 0 on success, 1 on error. int keyboard_finalize(void); diff --git a/kernel/inc/process/wait.h b/kernel/inc/process/wait.h index 152b3f37..1111b6b3 100644 --- a/kernel/inc/process/wait.h +++ b/kernel/inc/process/wait.h @@ -123,3 +123,10 @@ int default_wake_function(wait_queue_entry_t *entry, unsigned mode, int sync); /// @return Pointer to the entry inside the wq representing the /// sleeping process. wait_queue_entry_t *sleep_on(wait_queue_head_t *head); + +/// @brief Wake all tasks waiting on the queue. +/// +/// Calls each entry's wake function and removes entries whose function +/// returns non‑zero. This is the generic helper used by drivers when +/// data arrives. +void wake_up_all(wait_queue_head_t *head); diff --git a/kernel/src/drivers/keyboard/keyboard.c b/kernel/src/drivers/keyboard/keyboard.c index 8bb94c84..d80138b0 100644 --- a/kernel/src/drivers/keyboard/keyboard.c +++ b/kernel/src/drivers/keyboard/keyboard.c @@ -8,12 +8,13 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[KEYBRD]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "ctype.h" #include "descriptor_tables/isr.h" #include "drivers/keyboard/keyboard.h" +#include "process/wait.h" /* need sleep_on / wake_up_all */ #include "drivers/keyboard/keymap.h" #include "drivers/ps2.h" #include "hardware/pic8259.h" @@ -33,6 +34,11 @@ rb_keybuffer_t scancodes; /// Spinlock to protect access to the scancode buffer. spinlock_t scancodes_lock; +/* wait queue used by keyboard_pop_back(), initialized in + * keyboard_initialize(). Readers sleep here when the scancode buffer + * is empty; the ISR wakes them when a new key arrives. */ +static wait_queue_head_t keyboard_wait_queue; + #define KBD_LEFT_SHIFT (1 << 0) ///< Flag which identifies the left shift. #define KBD_RIGHT_SHIFT (1 << 1) ///< Flag which identifies the right shift. #define KBD_CAPS_LOCK (1 << 2) ///< Flag which identifies the caps lock. @@ -91,6 +97,9 @@ static inline void keyboard_push_back(unsigned int c) // Unlock the buffer after the push operation is complete. spinlock_unlock(&scancodes_lock); + + /* wake any readers waiting for input */ + wake_up_all(&keyboard_wait_queue); } /// @brief Pushes a character into the scancode ring buffer. @@ -105,6 +114,9 @@ static inline void keyboard_push_front(unsigned int c) // Unlock the buffer after the push operation is complete. spinlock_unlock(&scancodes_lock); + + /* wake readers as well; front or back both add data */ + wake_up_all(&keyboard_wait_queue); } /// @brief Pushes a sequence of characters (scancodes) into the keyboard buffer. @@ -143,12 +155,22 @@ static inline void keyboard_push_front_sequence(char *sequence) /// @return the value we removed from the ring buffer. int keyboard_pop_back(void) { + int c; + spinlock_lock(&scancodes_lock); - int c = rb_keybuffer_pop_back(&scancodes); + c = rb_keybuffer_pop_back(&scancodes); spinlock_unlock(&scancodes_lock); + + /* simply return what we have; caller decides what to do when empty */ return c; } +/// @brief Put the current task to sleep waiting for keyboard data. +void keyboard_wait(void) +{ + sleep_on(&keyboard_wait_queue); +} + int keyboard_peek_back(void) { spinlock_lock(&scancodes_lock); @@ -411,6 +433,8 @@ int keyboard_initialize(void) rb_keybuffer_init(&scancodes); // Initialize the spinlock. spinlock_init(&scancodes_lock); + // init wait queue used by readers + wait_queue_head_init(&keyboard_wait_queue); // Initialize the keymaps. init_keymaps(); // Install the IRQ. diff --git a/kernel/src/io/proc_video.c b/kernel/src/io/proc_video.c index b02cf732..2e11c4e6 100644 --- a/kernel/src/io/proc_video.c +++ b/kernel/src/io/proc_video.c @@ -88,7 +88,11 @@ static ssize_t procv_read(vfs_file_t *file, char *buf, off_t offset, size_t nbyt // Check that it's a valid character. if (c < 0) { - return 0; // No valid character received. + if ((file->flags & O_NONBLOCK) == 0) { + /* blocking descriptor; sleep until a key arrives. */ + keyboard_wait(); + } + return -EAGAIN; } // Keep only the character, not the scancode. diff --git a/kernel/src/process/wait.c b/kernel/src/process/wait.c index 92fef203..913d6fd1 100644 --- a/kernel/src/process/wait.c +++ b/kernel/src/process/wait.c @@ -12,9 +12,11 @@ #include "process/wait.h" #include "assert.h" +#include #include "mem/alloc/slab.h" #include "process/scheduler.h" #include "string.h" +#include "klib/irqflags.h" /* irq_disable/irq_enable */ /// @brief Adds the entry to the wait queue. /// @param head the wait queue. @@ -117,6 +119,24 @@ void wait_queue_entry_dealloc(wait_queue_entry_t *entry) kfree(entry); } +void wake_up_all(wait_queue_head_t *head) +{ + if (!head) { + pr_err("wake_up_all: head is NULL\n"); + return; + } + + // iterate through the queue and invoke each wake function + list_for_each_safe_decl(it, store, &head->task_list) { + wait_queue_entry_t *entry = list_entry(it, wait_queue_entry_t, task_list); + if (entry->func(entry, TASK_RUNNING, 0)) { + remove_wait_queue(head, entry); + pr_debug("wake_up_all: waking process %d\n", entry->task->pid); + wait_queue_entry_dealloc(entry); + } + } +} + void wait_queue_entry_init(wait_queue_entry_t *entry, struct task_struct *task) { // Validate the input. @@ -183,6 +203,16 @@ wait_queue_entry_t *sleep_on(wait_queue_head_t *head) return NULL; } + /* + * We want to avoid a race where an interrupt arrives between setting the + * task state to TASK_UNINTERRUPTIBLE and the caller adding the entry to + * the queue. If the wakeup occurs in that window the notification is + * lost and the task could sleep forever. The simple way to prevent this + * is to disable interrupts while changing the state and inserting the + * entry; IRQs are restored before returning so normal operation resumes. + */ + uint8_t irqs = irq_disable(); + // Set the task state to uninterruptible to indicate it is sleeping. sleeping_task->state = TASK_UNINTERRUPTIBLE; @@ -190,6 +220,7 @@ wait_queue_entry_t *sleep_on(wait_queue_head_t *head) wait_queue_entry_t *entry = wait_queue_entry_alloc(); if (!entry) { pr_err("Failed to allocate memory for wait queue entry.\n"); + irq_enable(irqs); return NULL; } @@ -199,6 +230,9 @@ wait_queue_entry_t *sleep_on(wait_queue_head_t *head) // Add the wait queue entry to the specified wait queue. add_wait_queue(head, entry); + /* restore interrupts before returning */ + irq_enable(irqs); + pr_debug("Added process %d to the wait queue.\n", sleeping_task->pid); return entry; diff --git a/lib/inc/stdio.h b/lib/inc/stdio.h index 2e4e856e..22a7e8c7 100644 --- a/lib/inc/stdio.h +++ b/lib/inc/stdio.h @@ -51,6 +51,19 @@ int fgetc(int fd); /// @param n The amount of characters to read. /// @param fd The file descriptor from which it reads. /// @return The read string. +/* + * Read up to n-1 characters from the file descriptor `fd` into `buf`, + * stopping after a newline or on end-of-file. The newline is retained + * and a null terminator is appended. The function returns `buf` on + * success, or NULL on error or EOF. + * + * WARNING: this implementation simply performs raw reads from the file + * descriptor. If `fd` refers to a terminal with ICANON disabled, read(2) + * may return as soon as any input is available; `fgets` will then return + * the data that was read rather than blocking for a full line. This + * mirrors standard POSIX semantics – applications that rely on line + * discipline should use canonical mode or perform their own buffering. + */ char *fgets(char *buf, int n, int fd); /// @brief Flushes the output buffer of a stream. diff --git a/lib/src/stdio.c b/lib/src/stdio.c index 260f817e..b403a1d1 100644 --- a/lib/src/stdio.c +++ b/lib/src/stdio.c @@ -18,10 +18,25 @@ void puts(const char *str) { write(STDOUT_FILENO, str, strlen(str)); } int getchar(void) { - char c = 0; - while (read(STDIN_FILENO, &c, 1) == 0) { + unsigned char c; + ssize_t r; + + while (1) { + r = read(STDIN_FILENO, &c, 1); + if (r > 0) { + return (int)c; + } + if (r == 0) { + /* EOF reached; keep looping in case more data comes later */ + continue; + } + /* error: for blocking descriptors we ignore EAGAIN/EWOULDBLOCK and + * retry; otherwise propagate EOF. */ + if (errno == EAGAIN || errno == EWOULDBLOCK) { + continue; + } + return EOF; } - return c; } char *gets(char *str) @@ -190,22 +205,38 @@ int fgetc(int fd) char c; ssize_t bytes_read; - // Read a single character from the file descriptor. - bytes_read = read(fd, &c, 1); + for (;;) { + // Read a single character from the file descriptor. + bytes_read = read(fd, &c, 1); - // Check for errors or EOF. - if (bytes_read == -1) { - perror("Error reading from file descriptor"); - return EOF; // Return EOF on error. - } - if (bytes_read == 0) { - return EOF; // Return EOF if no bytes were read (end of file). - } + // Check for errors or EOF. + if (bytes_read == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + continue; // retry on temporary unavailability + } + return EOF; // other error treated as EOF + } + if (bytes_read == 0) { + return EOF; // End of file + } - // Return the character as an unsigned char. - return (unsigned char)c; + // Return the character as an unsigned char. + return (unsigned char)c; + } } + +/* + * Simple fgets implementation used throughout userspace. It performs a + * byte‑oriented read(2) loop until a newline is observed, the buffer is + * full, or an EOF/error is returned. Note that the behaviour of the + * underlying read(2) call is outside of libc's control: if the descriptor + * refers to a terminal in non‑canonical mode, the kernel will deliver + * whatever bytes are available (typically a single keystroke) and return + * immediately. In that case fgets may return a partial line (or even NULL + * if no data is ready); programs wishing to receive complete lines should + * either leave ICANON enabled or implement their own buffering logic. + */ char *fgets(char *buf, int n, int fd) { char *p = buf; diff --git a/userspace/bin/login.c b/userspace/bin/login.c index 4403966b..3613e51f 100644 --- a/userspace/bin/login.c +++ b/userspace/bin/login.c @@ -75,8 +75,12 @@ static inline int __read_input(char *buffer, size_t size, int show) do { c = getchar(); // Read a character from input - // Ignore EOF and null or tab characters - if (c == EOF || c == 0 || c == '\t') { + // Detect error/EOF and abort early so caller can handle it. + if (c == EOF) { + return -1; + } + // Ignore null or tab characters + if (c == 0 || c == '\t') { continue; } From 185f7b857f1499234bc1d7a0e0f616865dc7934a Mon Sep 17 00:00:00 2001 From: "Enrico Fraccaroli (Galfurian)" Date: Fri, 27 Feb 2026 16:23:09 +0100 Subject: [PATCH 02/22] fix(scheduler): handle empty runqueue and null picks; add idle loop --- kernel/src/klib/assert.c | 4 ++-- kernel/src/process/scheduler.c | 20 +++++++++++++++++ kernel/src/process/scheduler_algorithm.c | 28 ++++++++++++++---------- kernel/src/process/wait.c | 5 +++++ kernel/src/system/panic.c | 3 +-- 5 files changed, 45 insertions(+), 15 deletions(-) diff --git a/kernel/src/klib/assert.c b/kernel/src/klib/assert.c index 789a709c..98833f3b 100644 --- a/kernel/src/klib/assert.c +++ b/kernel/src/klib/assert.c @@ -16,10 +16,10 @@ void __assert_fail(const char *assertion, const char *file, const char *function, unsigned int line) { pr_emerg( - "\n=== ASSERTION FAILED ===\n" + "=== ASSERTION FAILED ===\n" "Assertion: %s\n" "Location : %s:%d\n" - "Function : %s\n\n", + "Function : %s\n", assertion, file, line, (function ? function : "Unknown function")); kernel_panic("Assertion failed."); } diff --git a/kernel/src/process/scheduler.c b/kernel/src/process/scheduler.c index d8f64036..a5297dc7 100644 --- a/kernel/src/process/scheduler.c +++ b/kernel/src/process/scheduler.c @@ -162,6 +162,26 @@ void scheduler_run(pt_regs_t *f) #endif // Pointer to the next process to be executed. next = scheduler_pick_next_task(&runqueue); + + /* if no runnable task was found we may be in a situation where + * every user process is blocked. rather than assert or return + * to the blocked task we idle the cpu until some other task is + * enqueued; the scheduler_pick_next_task call above will have + * already re-enabled the cpu if necessary. */ + if (!next) { + if (runqueue.num_active == 0) { + while (runqueue.num_active == 0) { + sti(); + asm volatile("hlt"); + cli(); + } + next = scheduler_pick_next_task(&runqueue); + } + if (!next) { + /* still nothing, resume current (will likely block again) */ + next = runqueue.curr; + } + } //===================================================================== } // Check if the next and current processes are different. diff --git a/kernel/src/process/scheduler_algorithm.c b/kernel/src/process/scheduler_algorithm.c index 6b2e7c2d..fb475d13 100644 --- a/kernel/src/process/scheduler_algorithm.c +++ b/kernel/src/process/scheduler_algorithm.c @@ -39,9 +39,15 @@ static inline bool_t __is_periodic_task(task_struct *task) /// @return the next task on success, NULL on failure. static inline task_struct *__scheduler_rr(runqueue_t *runqueue, bool_t skip_periodic) { - // If there is just one task, return it; no need to do anything. + // If there is just one task in the list, we normally would return it. + // however, if the current task is not in a runnable state we must + // indicate that no suitable candidate exists. if (list_head_size(&runqueue->curr->run_list) <= 1) { - return runqueue->curr; + if (runqueue->curr->state == TASK_RUNNING) { + return runqueue->curr; + } else { + return NULL; + } } // This will hold a given entry, while iterating the list of tasks. task_struct *entry = NULL; @@ -381,17 +387,17 @@ task_struct *scheduler_pick_next_task(runqueue_t *runqueue) #error "You should enable a scheduling algorithm!" #endif - assert(next && "No valid task selected by the scheduling algorithm."); - - // Update the last context switch time of the next task. - next->se.exec_start = timer_get_ticks(); - + if (next != NULL) { + // Update the last context switch time of the next task. + next->se.exec_start = timer_get_ticks(); #ifdef ENABLE_SCHEDULER_FEEDBACK - // Update the feedback statistics for the new scheduled task. - scheduler_feedback_task_update(next); - // Update the overall feedback system. - scheduler_feedback_update(); + // Update the feedback statistics for the new scheduled task. + scheduler_feedback_task_update(next); + // Update the overall feedback system. + scheduler_feedback_update(); #endif + } + return next; } diff --git a/kernel/src/process/wait.c b/kernel/src/process/wait.c index 913d6fd1..20a4d574 100644 --- a/kernel/src/process/wait.c +++ b/kernel/src/process/wait.c @@ -132,6 +132,8 @@ void wake_up_all(wait_queue_head_t *head) if (entry->func(entry, TASK_RUNNING, 0)) { remove_wait_queue(head, entry); pr_debug("wake_up_all: waking process %d\n", entry->task->pid); + // reinsert into runqueue; it has been dequeued when it slept + scheduler_enqueue_task(entry->task); wait_queue_entry_dealloc(entry); } } @@ -216,6 +218,9 @@ wait_queue_entry_t *sleep_on(wait_queue_head_t *head) // Set the task state to uninterruptible to indicate it is sleeping. sleeping_task->state = TASK_UNINTERRUPTIBLE; + // Remove task from runqueue so scheduler will ignore it while blocked. + scheduler_dequeue_task(sleeping_task); + // Allocate memory for a new wait queue entry. wait_queue_entry_t *entry = wait_queue_entry_alloc(); if (!entry) { diff --git a/kernel/src/system/panic.c b/kernel/src/system/panic.c index abb32305..4d738cba 100644 --- a/kernel/src/system/panic.c +++ b/kernel/src/system/panic.c @@ -13,8 +13,7 @@ extern int runtests; void kernel_panic(const char *msg) { - pr_emerg("\nPANIC:\n%s\n\nWelcome to Kernel Debugging Land...\n\n", msg); - pr_emerg("\n"); + pr_emerg("\nPANIC:\n%s\n\nWelcome to Kernel Debugging Land...\n", msg); __asm__ __volatile__("cli"); // Disable interrupts if (runtests) { outports(SHUTDOWN_PORT, 0x2000); From ce36244240ae73310d6a19ce8f17c51088711ad8 Mon Sep 17 00:00:00 2001 From: "Enrico Fraccaroli (Galfurian)" Date: Fri, 27 Feb 2026 16:27:15 +0100 Subject: [PATCH 03/22] Standardize comments. --- kernel/src/klib/rbtree.c | 8 +- kernel/src/klib/string.c | 44 +-- kernel/src/process/scheduler.c | 16 +- kernel/src/process/wait.c | 19 +- lib/inc/ring_buffer.h | 692 ++++++++++++++++----------------- lib/inc/stdio.h | 13 - lib/src/abort.c | 12 +- lib/src/setenv.c | 4 +- lib/src/stdio.c | 10 +- 9 files changed, 387 insertions(+), 431 deletions(-) diff --git a/kernel/src/klib/rbtree.c b/kernel/src/klib/rbtree.c index 50fca44a..aed98545 100644 --- a/kernel/src/klib/rbtree.c +++ b/kernel/src/klib/rbtree.c @@ -500,7 +500,7 @@ int rbtree_tree_test(rbtree_t *tree, rbtree_node_t *root) rbtree_node_t *ln = root->link[0]; rbtree_node_t *rn = root->link[1]; - /* Consecutive red links */ + // Consecutive red links. if (rbtree_node_is_red(root)) { if (rbtree_node_is_red(ln) || rbtree_node_is_red(rn)) { pr_err("Red violation"); @@ -511,19 +511,19 @@ int rbtree_tree_test(rbtree_t *tree, rbtree_node_t *root) lh = rbtree_tree_test(tree, ln); rh = rbtree_tree_test(tree, rn); - /* Invalid binary search tree */ + // Invalid binary search tree. if ((ln != NULL && tree->cmp(tree, ln, root) >= 0) || (rn != NULL && tree->cmp(tree, rn, root) <= 0)) { pr_err("Binary tree violation"); return 0; } - /* Black height mismatch */ + // Black height mismatch. if (lh != 0 && rh != 0 && lh != rh) { pr_err("Black violation"); return 0; } - /* Only count black links */ + // Only count black links. if (lh != 0 && rh != 0) { return rbtree_node_is_red(root) ? lh : lh + 1; } diff --git a/kernel/src/klib/string.c b/kernel/src/klib/string.c index 7b2c3e59..bb2923bd 100644 --- a/kernel/src/klib/string.c +++ b/kernel/src/klib/string.c @@ -260,9 +260,7 @@ void *memmove(void *dst, const void *src, size_t n) void *ret = dst; if (dst <= src || (char *)dst >= ((char *)src + n)) { - /* Non-overlapping buffers; copy from lower addresses to higher - * addresses. - */ + // Non-overlapping buffers; copy from lower addresses to higher addresses. while (n--) { *(char *)dst = *(char *)src; dst = (char *)dst + 1; @@ -387,29 +385,23 @@ char *strtok_r(char *str, const char *delim, char **saveptr) map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7)); } while (*ctrl++); - /* Initialize s. If str is NULL, set s to the saved - * pointer (i.e., continue breaking tokens out of the str - * from the last strtok call). - */ + // Initialize s. If str is NULL, set s to the saved pointer (i.e., continue breaking tokens out of the str from the + // last strtok call). if (str) { s = str; } else { s = *saveptr; } - /* Find beginning of token (skip over leading delimiters). Note that - * there is no token iff this loop sets s to point to the terminal - * null (*s == '\0'). - */ + // Find beginning of token (skip over leading delimiters). Note that there is no token iff this loop sets s to point + // to the terminal null (*s == '\0'). while ((map[*s >> 3] & (1 << (*s & 7))) && *s) { s++; } str = s; - /* Find the end of the token. If it is not the end of the str, - * put a null there. - */ + // Find the end of the token. If it is not the end of the str, put a null there. for (; *s; s++) { if (map[*s >> 3] & (1 << (*s & 7))) { *s++ = '\0'; @@ -428,19 +420,6 @@ char *strtok_r(char *str, const char *delim, char **saveptr) return str; } -// Intrinsic functions. - -/* - * #pragma function(memset) - * #pragma function(memcmp) - * #pragma function(memcpy) - * #pragma function(strcpy) - * #pragma function(strlen) - * #pragma function(strcat) - * #pragma function(strcmp) - * #pragma function(strset) - */ - void *memset(void *ptr, int value, size_t num) { // Turn the pointer into a char * pointer. Here, we use the volatile keyword @@ -609,9 +588,7 @@ char *trim(char *str) len = strlen(str); endp = str + len; - /* Move the front and back pointers to address the first non-whitespace - * characters from each end. - */ + // Move the front and back pointers to address the first non-whitespace characters from each end. while (isspace((unsigned char)*frontp)) { ++frontp; } @@ -624,10 +601,9 @@ char *trim(char *str) } else if (frontp != str && endp == frontp) { *str = '\0'; } - /* Shift the string so that it starts at str so that if it's dynamically - * allocated, we can still free it on the returned pointer. Note the reuse - * of endp to mean the front of the string buffer now. - */ + + // Shift the string so that it starts at str so that if it's dynamically allocated, we can still free it on the + // returned pointer. Note the reuse of endp to mean the front of the string buffer now. endp = str; if (frontp != str) { while (*frontp) { diff --git a/kernel/src/process/scheduler.c b/kernel/src/process/scheduler.c index a5297dc7..04072d9d 100644 --- a/kernel/src/process/scheduler.c +++ b/kernel/src/process/scheduler.c @@ -4,10 +4,10 @@ /// See LICENSE.md for details. // Setup the logging for this file (do this before any other include). -#include "sys/kernel_levels.h" // Include kernel log levels. -#define __DEBUG_HEADER__ "[SCHED ]" ///< Change header. +#include "sys/kernel_levels.h" // Include kernel log levels. +#define __DEBUG_HEADER__ "[SCHED ]" ///< Change header. #define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. -#include "io/debug.h" // Include debugging functions. +#include "io/debug.h" // Include debugging functions. #include "assert.h" #include "descriptor_tables/tss.h" @@ -163,11 +163,9 @@ void scheduler_run(pt_regs_t *f) // Pointer to the next process to be executed. next = scheduler_pick_next_task(&runqueue); - /* if no runnable task was found we may be in a situation where - * every user process is blocked. rather than assert or return - * to the blocked task we idle the cpu until some other task is - * enqueued; the scheduler_pick_next_task call above will have - * already re-enabled the cpu if necessary. */ + // If no runnable task was found we may be in a situation where every user process is blocked. rather than + // assert or return to the blocked task we idle the cpu until some other task is enqueued; the + // scheduler_pick_next_task call above will have already re-enabled the cpu if necessary. if (!next) { if (runqueue.num_active == 0) { while (runqueue.num_active == 0) { @@ -178,7 +176,7 @@ void scheduler_run(pt_regs_t *f) next = scheduler_pick_next_task(&runqueue); } if (!next) { - /* still nothing, resume current (will likely block again) */ + // Still nothing, resume current (will likely block again). next = runqueue.curr; } } diff --git a/kernel/src/process/wait.c b/kernel/src/process/wait.c index 20a4d574..dea0b893 100644 --- a/kernel/src/process/wait.c +++ b/kernel/src/process/wait.c @@ -12,11 +12,11 @@ #include "process/wait.h" #include "assert.h" -#include +#include "klib/irqflags.h" #include "mem/alloc/slab.h" #include "process/scheduler.h" #include "string.h" -#include "klib/irqflags.h" /* irq_disable/irq_enable */ +#include /// @brief Adds the entry to the wait queue. /// @param head the wait queue. @@ -127,7 +127,8 @@ void wake_up_all(wait_queue_head_t *head) } // iterate through the queue and invoke each wake function - list_for_each_safe_decl(it, store, &head->task_list) { + list_for_each_safe_decl(it, store, &head->task_list) + { wait_queue_entry_t *entry = list_entry(it, wait_queue_entry_t, task_list); if (entry->func(entry, TASK_RUNNING, 0)) { remove_wait_queue(head, entry); @@ -205,14 +206,10 @@ wait_queue_entry_t *sleep_on(wait_queue_head_t *head) return NULL; } - /* - * We want to avoid a race where an interrupt arrives between setting the - * task state to TASK_UNINTERRUPTIBLE and the caller adding the entry to - * the queue. If the wakeup occurs in that window the notification is - * lost and the task could sleep forever. The simple way to prevent this - * is to disable interrupts while changing the state and inserting the - * entry; IRQs are restored before returning so normal operation resumes. - */ + // We want to avoid a race where an interrupt arrives between setting the task state to TASK_UNINTERRUPTIBLE and the + // caller adding the entry to the queue. If the wakeup occurs in that window the notification is lost and the task + // could sleep forever. The simple way to prevent this is to disable interrupts while changing the state and + // inserting the entry; IRQs are restored before returning so normal operation resumes. uint8_t irqs = irq_disable(); // Set the task state to uninterruptible to indicate it is sleeping. diff --git a/lib/inc/ring_buffer.h b/lib/inc/ring_buffer.h index 0fc099d2..f14b42a7 100644 --- a/lib/inc/ring_buffer.h +++ b/lib/inc/ring_buffer.h @@ -72,356 +72,356 @@ #pragma once /// @brief Declares a fixed-size ring buffer. -#define DECLARE_FIXED_SIZE_RING_BUFFER(type, name, length, init) \ - typedef struct { \ - type buffer[length]; \ - unsigned size; \ - unsigned head; \ - unsigned tail; \ - unsigned count; \ - } rb_##name##_t; \ - \ - static inline void rb_##name##_init(rb_##name##_t *rb) \ - { \ - rb->head = 0; \ - rb->tail = 0; \ - rb->count = 0; \ - rb->size = length; \ - for (unsigned i = 0; i < (length); i++) { \ - rb->buffer[i] = init; \ - } \ - } \ - \ - static inline unsigned rb_##name##_is_empty(rb_##name##_t *rb) { return rb->count == 0; } \ - \ - static inline unsigned rb_##name##_is_full(rb_##name##_t *rb) { return rb->count == rb->size; } \ - \ - static inline void rb_##name##_push_back(rb_##name##_t *rb, type item) \ - { \ - rb->buffer[rb->head] = item; \ - if (rb_##name##_is_full(rb)) { \ - rb->tail = (rb->tail + 1) % rb->size; \ - } else { \ - rb->count++; \ - } \ - rb->head = (rb->head + 1) % rb->size; \ - } \ - \ - static inline void rb_##name##_push_front(rb_##name##_t *rb, type item) \ - { \ - if (rb_##name##_is_full(rb)) { \ - rb->head = (rb->head == 0) ? rb->size - 1 : rb->head - 1; \ - } else { \ - rb->count++; \ - } \ - rb->tail = (rb->tail == 0) ? rb->size - 1 : rb->tail - 1; \ - rb->buffer[rb->tail] = item; \ - } \ - \ - static inline type rb_##name##_pop_front(rb_##name##_t *rb) \ - { \ - type item = init; \ - if (!rb_##name##_is_empty(rb)) { \ - item = rb->buffer[rb->tail]; \ - rb->buffer[rb->tail] = init; \ - rb->tail = (rb->tail + 1) % rb->size; \ - rb->count--; \ - } \ - return item; \ - } \ - \ - static inline type rb_##name##_pop_back(rb_##name##_t *rb) \ - { \ - type item = init; \ - if (!rb_##name##_is_empty(rb)) { \ - rb->head = (rb->head == 0) ? rb->size - 1 : rb->head - 1; \ - item = rb->buffer[rb->head]; \ - rb->buffer[rb->head] = init; \ - rb->count--; \ - } \ - return item; \ - } \ - \ - static inline type rb_##name##_peek_front(rb_##name##_t *rb) \ - { \ - if (rb_##name##_is_empty(rb)) { \ - return init; \ - } \ - return rb->buffer[rb->tail]; \ - } \ - \ - static inline type rb_##name##_peek_back(rb_##name##_t *rb) \ - { \ - if (rb_##name##_is_empty(rb)) { \ - return init; \ - } \ - return rb->buffer[(rb->head == 0) ? rb->size - 1 : rb->head - 1]; \ - } \ - \ - static inline type rb_##name##_get(rb_##name##_t *rb, unsigned position) \ - { \ - type item = init; \ - if (!rb_##name##_is_empty(rb) && (position < rb->size)) { \ - item = rb->buffer[(rb->tail + position) % rb->size]; \ - } \ - return item; \ - } \ - \ - static inline void rb_##name##_iterate(rb_##name##_t *rb, void (*callback)(type)) \ - { \ - for (unsigned i = 0; i < rb->count; i++) { \ - callback(rb_##name##_get(rb, i)); \ - } \ +#define DECLARE_FIXED_SIZE_RING_BUFFER(type, name, length, init) \ + typedef struct { \ + type buffer[length]; \ + unsigned size; \ + unsigned head; \ + unsigned tail; \ + unsigned count; \ + } rb_##name##_t; \ + \ + static inline void rb_##name##_init(rb_##name##_t *rb) \ + { \ + rb->head = 0; \ + rb->tail = 0; \ + rb->count = 0; \ + rb->size = length; \ + for (unsigned i = 0; i < (length); i++) { \ + rb->buffer[i] = init; \ + } \ + } \ + \ + static inline unsigned rb_##name##_is_empty(rb_##name##_t *rb) { return rb->count == 0; } \ + \ + static inline unsigned rb_##name##_is_full(rb_##name##_t *rb) { return rb->count == rb->size; } \ + \ + static inline void rb_##name##_push_back(rb_##name##_t *rb, type item) \ + { \ + rb->buffer[rb->head] = item; \ + if (rb_##name##_is_full(rb)) { \ + rb->tail = (rb->tail + 1) % rb->size; \ + } else { \ + rb->count++; \ + } \ + rb->head = (rb->head + 1) % rb->size; \ + } \ + \ + static inline void rb_##name##_push_front(rb_##name##_t *rb, type item) \ + { \ + if (rb_##name##_is_full(rb)) { \ + rb->head = (rb->head == 0) ? rb->size - 1 : rb->head - 1; \ + } else { \ + rb->count++; \ + } \ + rb->tail = (rb->tail == 0) ? rb->size - 1 : rb->tail - 1; \ + rb->buffer[rb->tail] = item; \ + } \ + \ + static inline type rb_##name##_pop_front(rb_##name##_t *rb) \ + { \ + type item = init; \ + if (!rb_##name##_is_empty(rb)) { \ + item = rb->buffer[rb->tail]; \ + rb->buffer[rb->tail] = init; \ + rb->tail = (rb->tail + 1) % rb->size; \ + rb->count--; \ + } \ + return item; \ + } \ + \ + static inline type rb_##name##_pop_back(rb_##name##_t *rb) \ + { \ + type item = init; \ + if (!rb_##name##_is_empty(rb)) { \ + rb->head = (rb->head == 0) ? rb->size - 1 : rb->head - 1; \ + item = rb->buffer[rb->head]; \ + rb->buffer[rb->head] = init; \ + rb->count--; \ + } \ + return item; \ + } \ + \ + static inline type rb_##name##_peek_front(rb_##name##_t *rb) \ + { \ + if (rb_##name##_is_empty(rb)) { \ + return init; \ + } \ + return rb->buffer[rb->tail]; \ + } \ + \ + static inline type rb_##name##_peek_back(rb_##name##_t *rb) \ + { \ + if (rb_##name##_is_empty(rb)) { \ + return init; \ + } \ + return rb->buffer[(rb->head == 0) ? rb->size - 1 : rb->head - 1]; \ + } \ + \ + static inline type rb_##name##_get(rb_##name##_t *rb, unsigned position) \ + { \ + type item = init; \ + if (!rb_##name##_is_empty(rb) && (position < rb->size)) { \ + item = rb->buffer[(rb->tail + position) % rb->size]; \ + } \ + return item; \ + } \ + \ + static inline void rb_##name##_iterate(rb_##name##_t *rb, void (*callback)(type)) \ + { \ + for (unsigned i = 0; i < rb->count; i++) { \ + callback(rb_##name##_get(rb, i)); \ + } \ } /// @brief Declares a dynamic-size ring-buffer. -#define DECLARE_DYNAMIC_SIZE_RING_BUFFER(type, name, init) \ - typedef struct { \ - type *buffer; \ - unsigned size; \ - unsigned head; \ - unsigned tail; \ - unsigned count; \ - } rb_##name##_t; \ - \ - static inline int rb_##name##_init(rb_##name##_t *rb, unsigned length, void *(*alloc_func)(size_t)) \ - { \ - rb->buffer = (type *)alloc_func(length * sizeof(type)); \ - if (!rb->buffer) { \ - return -1; /* Memory allocation failed */ \ - } \ - rb->head = 0; \ - rb->tail = 0; \ - rb->count = 0; \ - rb->size = length; \ - for (unsigned i = 0; i < length; i++) { \ - rb->buffer[i] = init; \ - } \ - return 0; /* Success */ \ - } \ - \ - static inline void rb_##name##_free(rb_##name##_t *rb, void (*free_func)(void *)) \ - { \ - if (rb->buffer) { \ - free_func(rb->buffer); \ - } \ - rb->buffer = 0; \ - rb->size = 0; \ - rb->head = 0; \ - rb->tail = 0; \ - rb->count = 0; \ - } \ - \ - static inline unsigned rb_##name##_is_empty(rb_##name##_t *rb) { return rb->count == 0; } \ - \ - static inline unsigned rb_##name##_is_full(rb_##name##_t *rb) { return rb->count == rb->size; } \ - \ - static inline void rb_##name##_push_back(rb_##name##_t *rb, type item) \ - { \ - rb->buffer[rb->head] = item; \ - if (rb_##name##_is_full(rb)) { \ - rb->tail = (rb->tail + 1) % rb->size; \ - } else { \ - rb->count++; \ - } \ - rb->head = (rb->head + 1) % rb->size; \ - } \ - \ - static inline void rb_##name##_push_front(rb_##name##_t *rb, type item) \ - { \ - if (rb_##name##_is_full(rb)) { \ - rb->head = (rb->head == 0) ? rb->size - 1 : rb->head - 1; \ - } else { \ - rb->count++; \ - } \ - rb->tail = (rb->tail == 0) ? rb->size - 1 : rb->tail - 1; \ - rb->buffer[rb->tail] = item; \ - } \ - \ - static inline type rb_##name##_pop_front(rb_##name##_t *rb) \ - { \ - if (rb_##name##_is_empty(rb)) { \ - return init; \ - } \ - type item = rb->buffer[rb->tail]; \ - rb->buffer[rb->tail] = init; \ - rb->tail = (rb->tail + 1) % rb->size; \ - rb->count--; \ - return item; \ - } \ - \ - static inline type rb_##name##_pop_back(rb_##name##_t *rb) \ - { \ - if (rb_##name##_is_empty(rb)) { \ - return init; \ - } \ - rb->head = (rb->head == 0) ? rb->size - 1 : rb->head - 1; \ - type item = rb->buffer[rb->head]; \ - rb->buffer[rb->head] = init; \ - rb->count--; \ - return item; \ - } \ - \ - static inline type rb_##name##_peek_front(rb_##name##_t *rb) \ - { \ - if (rb_##name##_is_empty(rb)) { \ - return init; \ - } \ - return rb->buffer[rb->tail]; \ - } \ - \ - static inline type rb_##name##_peek_back(rb_##name##_t *rb) \ - { \ - if (rb_##name##_is_empty(rb)) { \ - return init; \ - } \ - return rb->buffer[(rb->head == 0) ? rb->size - 1 : rb->head - 1]; \ - } \ - \ - static inline type rb_##name##_get(rb_##name##_t *rb, unsigned position) \ - { \ - if (rb_##name##_is_empty(rb) || (position >= rb->size)) { \ - return init; \ - } \ - return rb->buffer[(rb->tail + position) % rb->size]; \ - } \ - \ - static inline void rb_##name##_iterate(rb_##name##_t *rb, void (*callback)(type)) \ - { \ - for (unsigned i = 0; i < rb->count; i++) { \ - callback(rb_##name##_get(rb, i)); \ - } \ +#define DECLARE_DYNAMIC_SIZE_RING_BUFFER(type, name, init) \ + typedef struct { \ + type *buffer; \ + unsigned size; \ + unsigned head; \ + unsigned tail; \ + unsigned count; \ + } rb_##name##_t; \ + \ + static inline int rb_##name##_init(rb_##name##_t *rb, unsigned length, void *(*alloc_func)(size_t)) \ + { \ + rb->buffer = (type *)alloc_func(length * sizeof(type)); \ + if (!rb->buffer) { \ + return -1; \ + } \ + rb->head = 0; \ + rb->tail = 0; \ + rb->count = 0; \ + rb->size = length; \ + for (unsigned i = 0; i < length; i++) { \ + rb->buffer[i] = init; \ + } \ + return 0; \ + } \ + \ + static inline void rb_##name##_free(rb_##name##_t *rb, void (*free_func)(void *)) \ + { \ + if (rb->buffer) { \ + free_func(rb->buffer); \ + } \ + rb->buffer = 0; \ + rb->size = 0; \ + rb->head = 0; \ + rb->tail = 0; \ + rb->count = 0; \ + } \ + \ + static inline unsigned rb_##name##_is_empty(rb_##name##_t *rb) { return rb->count == 0; } \ + \ + static inline unsigned rb_##name##_is_full(rb_##name##_t *rb) { return rb->count == rb->size; } \ + \ + static inline void rb_##name##_push_back(rb_##name##_t *rb, type item) \ + { \ + rb->buffer[rb->head] = item; \ + if (rb_##name##_is_full(rb)) { \ + rb->tail = (rb->tail + 1) % rb->size; \ + } else { \ + rb->count++; \ + } \ + rb->head = (rb->head + 1) % rb->size; \ + } \ + \ + static inline void rb_##name##_push_front(rb_##name##_t *rb, type item) \ + { \ + if (rb_##name##_is_full(rb)) { \ + rb->head = (rb->head == 0) ? rb->size - 1 : rb->head - 1; \ + } else { \ + rb->count++; \ + } \ + rb->tail = (rb->tail == 0) ? rb->size - 1 : rb->tail - 1; \ + rb->buffer[rb->tail] = item; \ + } \ + \ + static inline type rb_##name##_pop_front(rb_##name##_t *rb) \ + { \ + if (rb_##name##_is_empty(rb)) { \ + return init; \ + } \ + type item = rb->buffer[rb->tail]; \ + rb->buffer[rb->tail] = init; \ + rb->tail = (rb->tail + 1) % rb->size; \ + rb->count--; \ + return item; \ + } \ + \ + static inline type rb_##name##_pop_back(rb_##name##_t *rb) \ + { \ + if (rb_##name##_is_empty(rb)) { \ + return init; \ + } \ + rb->head = (rb->head == 0) ? rb->size - 1 : rb->head - 1; \ + type item = rb->buffer[rb->head]; \ + rb->buffer[rb->head] = init; \ + rb->count--; \ + return item; \ + } \ + \ + static inline type rb_##name##_peek_front(rb_##name##_t *rb) \ + { \ + if (rb_##name##_is_empty(rb)) { \ + return init; \ + } \ + return rb->buffer[rb->tail]; \ + } \ + \ + static inline type rb_##name##_peek_back(rb_##name##_t *rb) \ + { \ + if (rb_##name##_is_empty(rb)) { \ + return init; \ + } \ + return rb->buffer[(rb->head == 0) ? rb->size - 1 : rb->head - 1]; \ + } \ + \ + static inline type rb_##name##_get(rb_##name##_t *rb, unsigned position) \ + { \ + if (rb_##name##_is_empty(rb) || (position >= rb->size)) { \ + return init; \ + } \ + return rb->buffer[(rb->tail + position) % rb->size]; \ + } \ + \ + static inline void rb_##name##_iterate(rb_##name##_t *rb, void (*callback)(type)) \ + { \ + for (unsigned i = 0; i < rb->count; i++) { \ + callback(rb_##name##_get(rb, i)); \ + } \ } /// @brief Declares a fixed-size 2d ring buffer. -#define DECLARE_FIXED_SIZE_2D_RING_BUFFER(type, name, size1, size2, init) \ - typedef struct { \ - type buffer[size2]; \ - unsigned size; \ - } rb_##name##_entry_t; \ - \ - void rb_##name##_entry_default_copy_fun(type *dest, const type *src, unsigned size) \ - { \ - for (unsigned i = 0; i < size; i++) { \ - dest[i] = src[i]; \ - } \ - } \ - \ - typedef struct { \ - rb_##name##_entry_t buffer[size1]; \ - unsigned size; \ - unsigned head; \ - unsigned tail; \ - unsigned count; \ - void (*copy)(type * dest, const type *src, unsigned); \ - } rb_##name##_t; \ - \ - static inline void rb_##name##_init_entry(rb_##name##_entry_t *entry) \ - { \ - entry->size = size2; \ - for (unsigned i = 0; i < (size2); i++) { \ - entry->buffer[i] = init; \ - } \ - } \ - \ - static inline void rb_##name##_init(rb_##name##_t *rb, void (*copy_fun)(type * dest, const type *src, unsigned)) \ - { \ - rb->head = 0; \ - rb->tail = 0; \ - rb->count = 0; \ - rb->size = size1; \ - if (copy_fun) { \ - rb->copy = copy_fun; \ - } else { \ - rb->copy = rb_##name##_entry_default_copy_fun; \ - } \ - for (unsigned i = 0; i < (size1); i++) { \ - rb_##name##_init_entry(rb->buffer + i); \ - } \ - } \ - \ - static inline unsigned rb_##name##_is_empty(rb_##name##_t *rb) { return rb->count == 0; } \ - \ - static inline unsigned rb_##name##_is_full(rb_##name##_t *rb) { return rb->count == (size1); } \ - \ - static inline void rb_##name##_push_back(rb_##name##_t *rb, rb_##name##_entry_t *item) \ - { \ - rb->copy(rb->buffer[rb->head].buffer, item->buffer, size2); \ - if (rb_##name##_is_full(rb)) { \ - rb->tail = (rb->tail + 1) % (size1); \ - } else { \ - rb->count++; \ - } \ - rb->head = (rb->head + 1) % (size1); \ - } \ - \ - static inline void rb_##name##_push_front(rb_##name##_t *rb, rb_##name##_entry_t *item) \ - { \ - if (rb_##name##_is_full(rb)) { \ - rb->head = (rb->head == 0) ? (size1) - 1 : rb->head - 1; \ - } else { \ - rb->count++; \ - } \ - rb->tail = (rb->tail == 0) ? (size1) - 1 : rb->tail - 1; \ - rb->copy(rb->buffer[rb->tail].buffer, item->buffer, size2); \ - } \ - \ - static inline int rb_##name##_pop_back(rb_##name##_t *rb, rb_##name##_entry_t *item) \ - { \ - if (rb_##name##_is_empty(rb)) { \ - return 1; \ - } \ - rb->head = (rb->head == 0) ? (size1) - 1 : rb->head - 1; \ - rb->copy(item->buffer, rb->buffer[rb->head].buffer, size2); \ - rb->count--; \ - return 0; \ - } \ - \ - static inline int rb_##name##_pop_front(rb_##name##_t *rb, rb_##name##_entry_t *item) \ - { \ - if (rb_##name##_is_empty(rb)) { \ - return 1; \ - } \ - rb->copy(item->buffer, rb->buffer[rb->tail].buffer, size2); \ - rb->tail = (rb->tail + 1) % (size1); \ - rb->count--; \ - return 0; \ - } \ - \ - static inline int rb_##name##_peek_back(rb_##name##_t *rb, rb_##name##_entry_t *item) \ - { \ - if (rb_##name##_is_empty(rb)) { \ - return 1; \ - } \ - unsigned index = ((rb->head == 0) ? (size1) - 1 : rb->head - 1); \ - rb->copy(item->buffer, rb->buffer[index].buffer, size2); \ - return 0; \ - } \ - \ - static inline int rb_##name##_peek_front(rb_##name##_t *rb, rb_##name##_entry_t *item) \ - { \ - if (rb_##name##_is_empty(rb)) { \ - return 1; \ - } \ - rb->copy(item->buffer, rb->buffer[rb->tail].buffer, size2); \ - return 0; \ - } \ - \ - static inline int rb_##name##_get(rb_##name##_t *rb, unsigned position, rb_##name##_entry_t *item) \ - { \ - if (!rb_##name##_is_empty(rb) && (position < rb->count)) { \ - unsigned index = (rb->tail + position) % (size1); \ - rb->copy(item->buffer, rb->buffer[index].buffer, size2); \ - return 1; \ - } \ - return 0; \ - } \ - \ - static inline void rb_##name##_iterate(rb_##name##_t *rb, void (*callback)(rb_##name##_entry_t *)) \ - { \ - rb_##name##_entry_t item; \ - for (unsigned i = 0; i < rb->count; i++) { \ - rb_##name##_get(rb, i, &item); \ - callback(&item); \ - } \ +#define DECLARE_FIXED_SIZE_2D_RING_BUFFER(type, name, size1, size2, init) \ + typedef struct { \ + type buffer[size2]; \ + unsigned size; \ + } rb_##name##_entry_t; \ + \ + void rb_##name##_entry_default_copy_fun(type *dest, const type *src, unsigned size) \ + { \ + for (unsigned i = 0; i < size; i++) { \ + dest[i] = src[i]; \ + } \ + } \ + \ + typedef struct { \ + rb_##name##_entry_t buffer[size1]; \ + unsigned size; \ + unsigned head; \ + unsigned tail; \ + unsigned count; \ + void (*copy)(type * dest, const type *src, unsigned); \ + } rb_##name##_t; \ + \ + static inline void rb_##name##_init_entry(rb_##name##_entry_t *entry) \ + { \ + entry->size = size2; \ + for (unsigned i = 0; i < (size2); i++) { \ + entry->buffer[i] = init; \ + } \ + } \ + \ + static inline void rb_##name##_init(rb_##name##_t *rb, void (*copy_fun)(type * dest, const type *src, unsigned)) \ + { \ + rb->head = 0; \ + rb->tail = 0; \ + rb->count = 0; \ + rb->size = size1; \ + if (copy_fun) { \ + rb->copy = copy_fun; \ + } else { \ + rb->copy = rb_##name##_entry_default_copy_fun; \ + } \ + for (unsigned i = 0; i < (size1); i++) { \ + rb_##name##_init_entry(rb->buffer + i); \ + } \ + } \ + \ + static inline unsigned rb_##name##_is_empty(rb_##name##_t *rb) { return rb->count == 0; } \ + \ + static inline unsigned rb_##name##_is_full(rb_##name##_t *rb) { return rb->count == (size1); } \ + \ + static inline void rb_##name##_push_back(rb_##name##_t *rb, rb_##name##_entry_t *item) \ + { \ + rb->copy(rb->buffer[rb->head].buffer, item->buffer, size2); \ + if (rb_##name##_is_full(rb)) { \ + rb->tail = (rb->tail + 1) % (size1); \ + } else { \ + rb->count++; \ + } \ + rb->head = (rb->head + 1) % (size1); \ + } \ + \ + static inline void rb_##name##_push_front(rb_##name##_t *rb, rb_##name##_entry_t *item) \ + { \ + if (rb_##name##_is_full(rb)) { \ + rb->head = (rb->head == 0) ? (size1) - 1 : rb->head - 1; \ + } else { \ + rb->count++; \ + } \ + rb->tail = (rb->tail == 0) ? (size1) - 1 : rb->tail - 1; \ + rb->copy(rb->buffer[rb->tail].buffer, item->buffer, size2); \ + } \ + \ + static inline int rb_##name##_pop_back(rb_##name##_t *rb, rb_##name##_entry_t *item) \ + { \ + if (rb_##name##_is_empty(rb)) { \ + return 1; \ + } \ + rb->head = (rb->head == 0) ? (size1) - 1 : rb->head - 1; \ + rb->copy(item->buffer, rb->buffer[rb->head].buffer, size2); \ + rb->count--; \ + return 0; \ + } \ + \ + static inline int rb_##name##_pop_front(rb_##name##_t *rb, rb_##name##_entry_t *item) \ + { \ + if (rb_##name##_is_empty(rb)) { \ + return 1; \ + } \ + rb->copy(item->buffer, rb->buffer[rb->tail].buffer, size2); \ + rb->tail = (rb->tail + 1) % (size1); \ + rb->count--; \ + return 0; \ + } \ + \ + static inline int rb_##name##_peek_back(rb_##name##_t *rb, rb_##name##_entry_t *item) \ + { \ + if (rb_##name##_is_empty(rb)) { \ + return 1; \ + } \ + unsigned index = ((rb->head == 0) ? (size1) - 1 : rb->head - 1); \ + rb->copy(item->buffer, rb->buffer[index].buffer, size2); \ + return 0; \ + } \ + \ + static inline int rb_##name##_peek_front(rb_##name##_t *rb, rb_##name##_entry_t *item) \ + { \ + if (rb_##name##_is_empty(rb)) { \ + return 1; \ + } \ + rb->copy(item->buffer, rb->buffer[rb->tail].buffer, size2); \ + return 0; \ + } \ + \ + static inline int rb_##name##_get(rb_##name##_t *rb, unsigned position, rb_##name##_entry_t *item) \ + { \ + if (!rb_##name##_is_empty(rb) && (position < rb->count)) { \ + unsigned index = (rb->tail + position) % (size1); \ + rb->copy(item->buffer, rb->buffer[index].buffer, size2); \ + return 1; \ + } \ + return 0; \ + } \ + \ + static inline void rb_##name##_iterate(rb_##name##_t *rb, void (*callback)(rb_##name##_entry_t *)) \ + { \ + rb_##name##_entry_t item; \ + for (unsigned i = 0; i < rb->count; i++) { \ + rb_##name##_get(rb, i, &item); \ + callback(&item); \ + } \ } diff --git a/lib/inc/stdio.h b/lib/inc/stdio.h index 22a7e8c7..2e4e856e 100644 --- a/lib/inc/stdio.h +++ b/lib/inc/stdio.h @@ -51,19 +51,6 @@ int fgetc(int fd); /// @param n The amount of characters to read. /// @param fd The file descriptor from which it reads. /// @return The read string. -/* - * Read up to n-1 characters from the file descriptor `fd` into `buf`, - * stopping after a newline or on end-of-file. The newline is retained - * and a null terminator is appended. The function returns `buf` on - * success, or NULL on error or EOF. - * - * WARNING: this implementation simply performs raw reads from the file - * descriptor. If `fd` refers to a terminal with ICANON disabled, read(2) - * may return as soon as any input is available; `fgets` will then return - * the data that was read rather than blocking for a full line. This - * mirrors standard POSIX semantics – applications that rely on line - * discipline should use canonical mode or perform their own buffering. - */ char *fgets(char *buf, int n, int fd); /// @brief Flushes the output buffer of a stream. diff --git a/lib/src/abort.c b/lib/src/abort.c index 4c329265..5349a303 100644 --- a/lib/src/abort.c +++ b/lib/src/abort.c @@ -19,7 +19,7 @@ void abort(void) struct sigaction action; sigset_t sigset; - /* Unblock SIGABRT. */ + // Unblock SIGABRT. if (stage == 0) { ++stage; @@ -28,7 +28,7 @@ void abort(void) sigprocmask(SIG_UNBLOCK, &sigset, 0); } - /* Send signal which possibly calls a user handler. */ + // Send signal which possibly calls a user handler. if (stage == 1) { // We must allow recursive calls of abort int save_stage = stage; @@ -38,7 +38,7 @@ void abort(void) stage = save_stage + 1; } - /* There was a handler installed. Now remove it. */ + // There was a handler installed. Now remove it. if (stage == 2) { ++stage; @@ -51,21 +51,21 @@ void abort(void) sigaction(SIGABRT, &action, NULL); } - /* Try again. */ + // Try again. if (stage == 3) { ++stage; kill(getpid(), SIGABRT); } - /* Now try to abort using the system specific command. */ + // Now try to abort using the system specific command. if (stage == 4) { ++stage; __asm__ __volatile__("hlt"); } - /* If we can't signal ourselves and the abort instruction failed, exit. */ + // If we can't signal ourselves and the abort instruction failed, exit. if (stage == 5) { ++stage; diff --git a/lib/src/setenv.c b/lib/src/setenv.c index ae4924e9..c3251b87 100644 --- a/lib/src/setenv.c +++ b/lib/src/setenv.c @@ -132,12 +132,12 @@ int unsetenv(const char *name) char **ep = environ; while (*ep != NULL) { if (!strncmp(*ep, name, len) && (*ep)[len] == '=') { - /* Found it. Remove this pointer by moving later ones back. */ + // Found it. Remove this pointer by moving later ones back. char **dp = ep; do { dp[0] = dp[1]; } while (*dp++); - /* Continue the loop in case NAME appears again. */ + // Continue the loop in case NAME appears again. } else { ++ep; } diff --git a/lib/src/stdio.c b/lib/src/stdio.c index b403a1d1..0570c129 100644 --- a/lib/src/stdio.c +++ b/lib/src/stdio.c @@ -27,11 +27,10 @@ int getchar(void) return (int)c; } if (r == 0) { - /* EOF reached; keep looping in case more data comes later */ + // EOF reached; keep looping in case more data comes later continue; } - /* error: for blocking descriptors we ignore EAGAIN/EWOULDBLOCK and - * retry; otherwise propagate EOF. */ + // error: for blocking descriptors we ignore EAGAIN/EWOULDBLOCK and retry; otherwise propagate EOF. if (errno == EAGAIN || errno == EWOULDBLOCK) { continue; } @@ -225,7 +224,6 @@ int fgetc(int fd) } } - /* * Simple fgets implementation used throughout userspace. It performs a * byte‑oriented read(2) loop until a newline is observed, the buffer is @@ -278,12 +276,12 @@ int fflush(int fd) // If fd is negative, the standard requires flushing all open output // streams, but since we don't maintain a list of open streams and // writes are unbuffered, we simply return success. - + // For a valid file descriptor, we could verify it exists, but for // simplicity and compatibility, we just return success. // If buffering is added in the future, this function should be updated // to actually flush any pending data. - + (void)fd; // Mark parameter as intentionally unused return 0; // Success } From 79f2ff8b35fb73874cd195186994ba44b6963eb8 Mon Sep 17 00:00:00 2001 From: "Enrico Fraccaroli (Galfurian)" Date: Fri, 27 Feb 2026 16:49:37 +0100 Subject: [PATCH 04/22] docs(wait): improve logging, prevent duplicate queue entries --- kernel/src/drivers/keyboard/keyboard.c | 4 ++-- kernel/src/process/wait.c | 26 +++++++++++++++++++------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/kernel/src/drivers/keyboard/keyboard.c b/kernel/src/drivers/keyboard/keyboard.c index d80138b0..08f5d4b8 100644 --- a/kernel/src/drivers/keyboard/keyboard.c +++ b/kernel/src/drivers/keyboard/keyboard.c @@ -8,19 +8,19 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[KEYBRD]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "ctype.h" #include "descriptor_tables/isr.h" #include "drivers/keyboard/keyboard.h" -#include "process/wait.h" /* need sleep_on / wake_up_all */ #include "drivers/keyboard/keymap.h" #include "drivers/ps2.h" #include "hardware/pic8259.h" #include "io/port_io.h" #include "io/video.h" #include "process/scheduler.h" +#include "process/wait.h" #include "ring_buffer.h" #include "string.h" #include "sys/bitops.h" diff --git a/kernel/src/process/wait.c b/kernel/src/process/wait.c index dea0b893..14aa6a70 100644 --- a/kernel/src/process/wait.c +++ b/kernel/src/process/wait.c @@ -96,7 +96,7 @@ wait_queue_entry_t *wait_queue_entry_alloc(void) { // Allocate the memory. wait_queue_entry_t *entry = (wait_queue_entry_t *)kmalloc(sizeof(wait_queue_entry_t)); - pr_debug("ALLOCATE wait_queue_entry_t 0x%p\n", entry); + pr_debug("ALLOCATE wait_queue_entry_t %p\n", entry); // Check the allocated memory. assert(entry && "Failed to allocate memory for a wait_queue_entry_t."); // Clean the memory. @@ -114,7 +114,7 @@ wait_queue_entry_t *wait_queue_entry_alloc(void) void wait_queue_entry_dealloc(wait_queue_entry_t *entry) { assert(entry && "Received a NULL pointer."); - pr_debug("FREE wait_queue_entry_t 0x%p\n", entry); + pr_debug("FREE wait_queue_entry_t %p\n", entry); // Deallocate the memory. kfree(entry); } @@ -130,11 +130,13 @@ void wake_up_all(wait_queue_head_t *head) list_for_each_safe_decl(it, store, &head->task_list) { wait_queue_entry_t *entry = list_entry(it, wait_queue_entry_t, task_list); - if (entry->func(entry, TASK_RUNNING, 0)) { + if (entry->func(entry, TASK_RUNNING, 0) || entry->task->state == TASK_RUNNING) { remove_wait_queue(head, entry); - pr_debug("wake_up_all: waking process %d\n", entry->task->pid); - // reinsert into runqueue; it has been dequeued when it slept - scheduler_enqueue_task(entry->task); + pr_debug("wake_up_all(%p): waking process %d\n", head, entry->task->pid); + /* only enqueue if not already on runqueue */ + if (entry->task->run_list.next == &entry->task->run_list) { + scheduler_enqueue_task(entry->task); + } wait_queue_entry_dealloc(entry); } } @@ -218,6 +220,16 @@ wait_queue_entry_t *sleep_on(wait_queue_head_t *head) // Remove task from runqueue so scheduler will ignore it while blocked. scheduler_dequeue_task(sleeping_task); + // If the process already has an entry on this queue, reuse it. + list_for_each_decl(it, &head->task_list) { + wait_queue_entry_t *e = list_entry(it, wait_queue_entry_t, task_list); + if (e->task == sleeping_task) { + /* already registered, no allocation necessary */ + irq_enable(irqs); + return e; + } + } + // Allocate memory for a new wait queue entry. wait_queue_entry_t *entry = wait_queue_entry_alloc(); if (!entry) { @@ -235,7 +247,7 @@ wait_queue_entry_t *sleep_on(wait_queue_head_t *head) /* restore interrupts before returning */ irq_enable(irqs); - pr_debug("Added process %d to the wait queue.\n", sleeping_task->pid); + pr_debug("Added process %d to wait queue %p (entry %p)\n", sleeping_task->pid, head, entry); return entry; } From 098d35699e2dc341fe055ec419f634dcf1599a56 Mon Sep 17 00:00:00 2001 From: "Enrico Fraccaroli (Galfurian)" Date: Fri, 27 Feb 2026 16:57:50 +0100 Subject: [PATCH 05/22] fix(scheduler): add idle hlt loop in empty runqueue --- kernel/src/process/scheduler.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/src/process/scheduler.c b/kernel/src/process/scheduler.c index 04072d9d..32eff37a 100644 --- a/kernel/src/process/scheduler.c +++ b/kernel/src/process/scheduler.c @@ -170,7 +170,7 @@ void scheduler_run(pt_regs_t *f) if (runqueue.num_active == 0) { while (runqueue.num_active == 0) { sti(); - asm volatile("hlt"); + __asm__ __volatile__("hlt"); cli(); } next = scheduler_pick_next_task(&runqueue); From 49876ab61c16e4624f4b6a5311d32054f49b50f5 Mon Sep 17 00:00:00 2001 From: "Enrico Fraccaroli (Galfurian)" Date: Fri, 27 Feb 2026 17:05:50 +0100 Subject: [PATCH 06/22] fix(mm): copy source page directory in mm_clone --- kernel/src/mem/mm/mm.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/kernel/src/mem/mm/mm.c b/kernel/src/mem/mm/mm.c index 712b8026..e17a0d68 100644 --- a/kernel/src/mem/mm/mm.c +++ b/kernel/src/mem/mm/mm.c @@ -114,11 +114,11 @@ mm_struct_t *mm_clone(mm_struct_t *mmp) // Copy the contents of the source mm_struct to the new one. memcpy(mm, mmp, sizeof(mm_struct_t)); - // Get the main page directory. - page_directory_t *main_pgd = paging_get_main_pgd(); - // Error handling: Failed to get the main page directory. - if (!main_pgd) { - pr_crit("Failed to get the main page directory\n"); + // Get the source process page directory. + page_directory_t *src_pgd = (page_directory_t *)mmp->pgd; + if (!src_pgd) { + pr_crit("Invalid source pgd in mm_clone\n"); + kmem_cache_free(mm); return NULL; } @@ -131,8 +131,8 @@ mm_struct_t *mm_clone(mm_struct_t *mmp) return NULL; } - // Initialize the new page directory by copying from the main directory. - memcpy(pdir_cpy, main_pgd, sizeof(page_directory_t)); + // Initialize the new page directory by copying from the source directory. + memcpy(pdir_cpy, src_pgd, sizeof(page_directory_t)); // Assign the copied page directory to the mm_struct. mm->pgd = pdir_cpy; From 9090e93dee1846922b595e99a0d5bd94a6f8570d Mon Sep 17 00:00:00 2001 From: "Enrico Fraccaroli (Galfurian)" Date: Fri, 27 Feb 2026 17:10:18 +0100 Subject: [PATCH 07/22] =?UTF-8?q?fix(mm):=20revert=20pd=20clone=20to=20mai?= =?UTF-8?q?n=C2=A0pgd=20and=20zero=20user=20entries?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/mem/mm/mm.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/kernel/src/mem/mm/mm.c b/kernel/src/mem/mm/mm.c index e17a0d68..7915e8ef 100644 --- a/kernel/src/mem/mm/mm.c +++ b/kernel/src/mem/mm/mm.c @@ -114,27 +114,33 @@ mm_struct_t *mm_clone(mm_struct_t *mmp) // Copy the contents of the source mm_struct to the new one. memcpy(mm, mmp, sizeof(mm_struct_t)); - // Get the source process page directory. - page_directory_t *src_pgd = (page_directory_t *)mmp->pgd; - if (!src_pgd) { - pr_crit("Invalid source pgd in mm_clone\n"); + // We deliberately **do not** copy the parent's page directory verbatim because that would reproduce every + // user‑space mapping in the child. vm_area_clone()/mem_clone_vm_area below will populate the child's page tables + // explicitly, which gives us better control and avoids aliasing the same physical page tables between processes. + // + // Instead we copy the *kernel* portion of the main page directory and leave the user half empty. + page_directory_t *main_pgd = paging_get_main_pgd(); + if (!main_pgd) { + pr_crit("Failed to get main page directory\n"); kmem_cache_free(mm); return NULL; } - // Allocate a new page directory to avoid data races on page tables. page_directory_t *pdir_cpy = kmem_cache_alloc(pgdir_cache, GFP_KERNEL); if (!pdir_cpy) { pr_crit("Failed to allocate page directory for new process.\n"); - // Free the previously allocated mm_struct. kmem_cache_free(mm); return NULL; } - // Initialize the new page directory by copying from the source directory. - memcpy(pdir_cpy, src_pgd, sizeof(page_directory_t)); + /* copy entire main directory then clear user entries */ + memcpy(pdir_cpy, main_pgd, sizeof(page_directory_t)); + for (int i = 0; i < 768; ++i) { + /* user entries start at 0, clear them */ + pdir_cpy->entries[i].present = 0; + pdir_cpy->entries[i].frame = 0; + } - // Assign the copied page directory to the mm_struct. mm->pgd = pdir_cpy; vm_area_struct_t *vm_area = NULL; From b774d5b38866887f33e15c94cacd98b7faa06d8d Mon Sep 17 00:00:00 2001 From: "Enrico Fraccaroli (Galfurian)" Date: Mon, 2 Mar 2026 13:56:29 +0100 Subject: [PATCH 08/22] fix(mm): revert page directory clone and zero user entries --- kernel/src/mem/mm/mm.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/kernel/src/mem/mm/mm.c b/kernel/src/mem/mm/mm.c index 7915e8ef..a66f510d 100644 --- a/kernel/src/mem/mm/mm.c +++ b/kernel/src/mem/mm/mm.c @@ -121,26 +121,23 @@ mm_struct_t *mm_clone(mm_struct_t *mmp) // Instead we copy the *kernel* portion of the main page directory and leave the user half empty. page_directory_t *main_pgd = paging_get_main_pgd(); if (!main_pgd) { - pr_crit("Failed to get main page directory\n"); - kmem_cache_free(mm); + pr_crit("Failed to get the main page directory\n"); return NULL; } + // Allocate a new page directory to avoid data races on page tables. page_directory_t *pdir_cpy = kmem_cache_alloc(pgdir_cache, GFP_KERNEL); if (!pdir_cpy) { pr_crit("Failed to allocate page directory for new process.\n"); + // Free the previously allocated mm_struct. kmem_cache_free(mm); return NULL; } - /* copy entire main directory then clear user entries */ + // Initialize the new page directory by copying from the main directory. memcpy(pdir_cpy, main_pgd, sizeof(page_directory_t)); - for (int i = 0; i < 768; ++i) { - /* user entries start at 0, clear them */ - pdir_cpy->entries[i].present = 0; - pdir_cpy->entries[i].frame = 0; - } + // Assign the copied page directory to the mm_struct. mm->pgd = pdir_cpy; vm_area_struct_t *vm_area = NULL; From fca9a4a02d558ebc504cabc508a2600063e6b981 Mon Sep 17 00:00:00 2001 From: "Enrico Fraccaroli (Galfurian)" Date: Mon, 2 Mar 2026 14:50:52 +0100 Subject: [PATCH 09/22] fix(scheduler): properly remove tasks from wait queues on signal delivery - Update sleep_on() to track which wait queue a task is sleeping on via task->waiting_on field - Update wake_up_all() to clear waiting_on field when removing task from wait queue - Update sys_kill() to properly remove task from its wait queue before re-enqueueing to runqueue - This prevents tasks from being on both wait queue and runqueue simultaneously, which was causing the shell to hang after child process execution --- boot/src/multiboot.c | 2 +- kernel/inc/process/process.h | 3 ++ kernel/src/crypt/sha256.c | 2 +- kernel/src/descriptor_tables/exception.c | 2 +- kernel/src/descriptor_tables/gdt.c | 2 +- kernel/src/descriptor_tables/idt.c | 2 +- kernel/src/descriptor_tables/interrupt.c | 2 +- kernel/src/descriptor_tables/tss.c | 2 +- kernel/src/devices/fpu.c | 2 +- kernel/src/devices/pci.c | 2 +- kernel/src/drivers/ata.c | 2 +- kernel/src/drivers/fdc.c | 2 +- kernel/src/drivers/keyboard/keyboard.c | 2 +- kernel/src/drivers/mem.c | 2 +- kernel/src/drivers/mouse.c | 2 +- kernel/src/drivers/ps2.c | 2 +- kernel/src/drivers/rtc.c | 2 +- kernel/src/elf/elf.c | 2 +- kernel/src/fs/ext2.c | 2 +- kernel/src/fs/namei.c | 2 +- kernel/src/fs/pipe.c | 2 +- kernel/src/fs/procfs.c | 2 +- kernel/src/fs/vfs.c | 2 +- kernel/src/hardware/pic8259.c | 2 +- kernel/src/hardware/timer.c | 2 +- kernel/src/io/proc_video.c | 2 +- kernel/src/io/video.c | 2 +- kernel/src/ipc/msg.c | 2 +- kernel/src/ipc/sem.c | 2 +- kernel/src/ipc/shm.c | 2 +- kernel/src/kernel.c | 2 +- kernel/src/klib/assert.c | 2 +- kernel/src/mem/alloc/buddy_system.c | 2 +- kernel/src/mem/alloc/heap.c | 2 +- kernel/src/mem/alloc/slab.c | 2 +- kernel/src/mem/alloc/zone_allocator.c | 2 +- kernel/src/mem/mm/mm.c | 2 +- kernel/src/mem/mm/page.c | 2 +- kernel/src/mem/mm/vm_area.c | 2 +- kernel/src/mem/mm/vmem.c | 2 +- kernel/src/mem/page_fault.c | 2 +- kernel/src/mem/paging.c | 2 +- kernel/src/multiboot.c | 2 +- kernel/src/process/process.c | 2 +- kernel/src/process/scheduler.c | 52 +++++++++++++----- kernel/src/process/scheduler_algorithm.c | 2 +- kernel/src/process/wait.c | 37 ++++++++++--- kernel/src/resource_tracing.c | 2 +- kernel/src/sys/module.c | 2 +- kernel/src/sys/utsname.c | 2 +- kernel/src/system/signal.c | 54 ++++++++++++++++++- kernel/src/system/syscall.c | 2 +- kernel/src/tests/runner.c | 2 +- kernel/src/tests/unit/test_buddy.c | 2 +- kernel/src/tests/unit/test_dma.c | 2 +- kernel/src/tests/unit/test_fpu.c | 2 +- kernel/src/tests/unit/test_gdt.c | 2 +- kernel/src/tests/unit/test_idt.c | 2 +- kernel/src/tests/unit/test_isr.c | 2 +- .../src/tests/unit/test_memory_adversarial.c | 2 +- kernel/src/tests/unit/test_mm.c | 2 +- kernel/src/tests/unit/test_page.c | 2 +- kernel/src/tests/unit/test_paging.c | 2 +- kernel/src/tests/unit/test_scheduler.c | 2 +- kernel/src/tests/unit/test_slab.c | 2 +- kernel/src/tests/unit/test_vmem.c | 2 +- kernel/src/tests/unit/test_zone_allocator.c | 2 +- 67 files changed, 185 insertions(+), 87 deletions(-) diff --git a/boot/src/multiboot.c b/boot/src/multiboot.c index 36e5f820..61c57592 100644 --- a/boot/src/multiboot.c +++ b/boot/src/multiboot.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[MTBOOT]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "kernel.h" diff --git a/kernel/inc/process/process.h b/kernel/inc/process/process.h index 4e760034..b748e2ef 100644 --- a/kernel/inc/process/process.h +++ b/kernel/inc/process/process.h @@ -158,6 +158,9 @@ typedef struct task_struct { /// Buffer for managing inputs from keyboard. rb_keybuffer_t keyboard_rb; + /// Wait queue this task is currently sleeping on (NULL if not sleeping). + struct wait_queue_head *waiting_on; + //==== Future work ========================================================= // - task's attributes: // struct task_struct __rcu *real_parent; diff --git a/kernel/src/crypt/sha256.c b/kernel/src/crypt/sha256.c index 913fdc96..d8d6397e 100644 --- a/kernel/src/crypt/sha256.c +++ b/kernel/src/crypt/sha256.c @@ -13,7 +13,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[SHA256]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "crypt/sha256.h" diff --git a/kernel/src/descriptor_tables/exception.c b/kernel/src/descriptor_tables/exception.c index 1f8b07e5..5000ca4f 100644 --- a/kernel/src/descriptor_tables/exception.c +++ b/kernel/src/descriptor_tables/exception.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[EXEPT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "descriptor_tables/idt.h" diff --git a/kernel/src/descriptor_tables/gdt.c b/kernel/src/descriptor_tables/gdt.c index 9bc8166a..a66ce75a 100644 --- a/kernel/src/descriptor_tables/gdt.c +++ b/kernel/src/descriptor_tables/gdt.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[GDT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "descriptor_tables/gdt.h" diff --git a/kernel/src/descriptor_tables/idt.c b/kernel/src/descriptor_tables/idt.c index d64557cd..09673d4c 100644 --- a/kernel/src/descriptor_tables/idt.c +++ b/kernel/src/descriptor_tables/idt.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[IDT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "descriptor_tables/gdt.h" diff --git a/kernel/src/descriptor_tables/interrupt.c b/kernel/src/descriptor_tables/interrupt.c index 55662610..eb332a8a 100644 --- a/kernel/src/descriptor_tables/interrupt.c +++ b/kernel/src/descriptor_tables/interrupt.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[IRQ ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/descriptor_tables/tss.c b/kernel/src/descriptor_tables/tss.c index 022c210a..a41a7c84 100644 --- a/kernel/src/descriptor_tables/tss.c +++ b/kernel/src/descriptor_tables/tss.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TSS ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "descriptor_tables/gdt.h" diff --git a/kernel/src/devices/fpu.c b/kernel/src/devices/fpu.c index e7588484..3227ed17 100644 --- a/kernel/src/devices/fpu.c +++ b/kernel/src/devices/fpu.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[FPU ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/devices/pci.c b/kernel/src/devices/pci.c index 95e5277e..75f6a83f 100644 --- a/kernel/src/devices/pci.c +++ b/kernel/src/devices/pci.c @@ -7,7 +7,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[PCI ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "devices/pci.h" diff --git a/kernel/src/drivers/ata.c b/kernel/src/drivers/ata.c index a728af6f..2212538f 100644 --- a/kernel/src/drivers/ata.c +++ b/kernel/src/drivers/ata.c @@ -8,7 +8,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[ATA ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "drivers/ata/ata.h" diff --git a/kernel/src/drivers/fdc.c b/kernel/src/drivers/fdc.c index c2829dd1..81ae98c3 100644 --- a/kernel/src/drivers/fdc.c +++ b/kernel/src/drivers/fdc.c @@ -8,7 +8,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[FDC ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "drivers/fdc.h" diff --git a/kernel/src/drivers/keyboard/keyboard.c b/kernel/src/drivers/keyboard/keyboard.c index 08f5d4b8..c9a2d1c6 100644 --- a/kernel/src/drivers/keyboard/keyboard.c +++ b/kernel/src/drivers/keyboard/keyboard.c @@ -8,7 +8,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[KEYBRD]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "ctype.h" diff --git a/kernel/src/drivers/mem.c b/kernel/src/drivers/mem.c index 1b316f65..b49f918f 100644 --- a/kernel/src/drivers/mem.c +++ b/kernel/src/drivers/mem.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[MEMDEV]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/drivers/mouse.c b/kernel/src/drivers/mouse.c index 533c33a9..f227e214 100644 --- a/kernel/src/drivers/mouse.c +++ b/kernel/src/drivers/mouse.c @@ -8,7 +8,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[MOUSE ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "descriptor_tables/isr.h" diff --git a/kernel/src/drivers/ps2.c b/kernel/src/drivers/ps2.c index fb9e642d..43f79ac7 100644 --- a/kernel/src/drivers/ps2.c +++ b/kernel/src/drivers/ps2.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[PS/2 ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "drivers/ps2.h" diff --git a/kernel/src/drivers/rtc.c b/kernel/src/drivers/rtc.c index 6ad21dc8..cf5aa29d 100644 --- a/kernel/src/drivers/rtc.c +++ b/kernel/src/drivers/rtc.c @@ -8,7 +8,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[RTC ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "descriptor_tables/isr.h" diff --git a/kernel/src/elf/elf.c b/kernel/src/elf/elf.c index 5848f6eb..706c938a 100644 --- a/kernel/src/elf/elf.c +++ b/kernel/src/elf/elf.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[ELF ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/fs/ext2.c b/kernel/src/fs/ext2.c index f21020bd..18728009 100644 --- a/kernel/src/fs/ext2.c +++ b/kernel/src/fs/ext2.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[EXT2 ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. // If defined, ETX2 will debug everything. // #define EXT2_FULL_DEBUG diff --git a/kernel/src/fs/namei.c b/kernel/src/fs/namei.c index 4c22c534..2821442d 100644 --- a/kernel/src/fs/namei.c +++ b/kernel/src/fs/namei.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[NAMEI ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/fs/pipe.c b/kernel/src/fs/pipe.c index 93b37749..0136e8f2 100644 --- a/kernel/src/fs/pipe.c +++ b/kernel/src/fs/pipe.c @@ -12,7 +12,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[PIPE ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. // ============================================================================ diff --git a/kernel/src/fs/procfs.c b/kernel/src/fs/procfs.c index 3e208501..326c0fd9 100644 --- a/kernel/src/fs/procfs.c +++ b/kernel/src/fs/procfs.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[PROCFS]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/fs/vfs.c b/kernel/src/fs/vfs.c index a5e07746..fa272071 100644 --- a/kernel/src/fs/vfs.c +++ b/kernel/src/fs/vfs.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[VFS ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/hardware/pic8259.c b/kernel/src/hardware/pic8259.c index abd98b00..c4e69c32 100644 --- a/kernel/src/hardware/pic8259.c +++ b/kernel/src/hardware/pic8259.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[PIC ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "hardware/pic8259.h" diff --git a/kernel/src/hardware/timer.c b/kernel/src/hardware/timer.c index c9ac1a36..1ebb73b3 100644 --- a/kernel/src/hardware/timer.c +++ b/kernel/src/hardware/timer.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TIMER ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/io/proc_video.c b/kernel/src/io/proc_video.c index 2e11c4e6..0c5a4b7f 100644 --- a/kernel/src/io/proc_video.c +++ b/kernel/src/io/proc_video.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[PROCV ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "bits/ioctls.h" diff --git a/kernel/src/io/video.c b/kernel/src/io/video.c index 1571258c..c7c347dd 100644 --- a/kernel/src/io/video.c +++ b/kernel/src/io/video.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" #define __DEBUG_HEADER__ "[VIDEO ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" #include "ctype.h" diff --git a/kernel/src/ipc/msg.c b/kernel/src/ipc/msg.c index 89ccdf6f..3abcc719 100644 --- a/kernel/src/ipc/msg.c +++ b/kernel/src/ipc/msg.c @@ -7,7 +7,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[IPCmsg]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. // ============================================================================ diff --git a/kernel/src/ipc/sem.c b/kernel/src/ipc/sem.c index 47a32762..326aa32e 100644 --- a/kernel/src/ipc/sem.c +++ b/kernel/src/ipc/sem.c @@ -34,7 +34,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[IPCsem]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. // ============================================================================ diff --git a/kernel/src/ipc/shm.c b/kernel/src/ipc/shm.c index 3e3e2654..3f6d7b15 100644 --- a/kernel/src/ipc/shm.c +++ b/kernel/src/ipc/shm.c @@ -7,7 +7,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[IPCshm]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. // ============================================================================ diff --git a/kernel/src/kernel.c b/kernel/src/kernel.c index 6feb4146..bcea0e82 100644 --- a/kernel/src/kernel.c +++ b/kernel/src/kernel.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[KERNEL]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "kernel.h" diff --git a/kernel/src/klib/assert.c b/kernel/src/klib/assert.c index 98833f3b..c1637230 100644 --- a/kernel/src/klib/assert.c +++ b/kernel/src/klib/assert.c @@ -10,7 +10,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[ASSERT]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. void __assert_fail(const char *assertion, const char *file, const char *function, unsigned int line) diff --git a/kernel/src/mem/alloc/buddy_system.c b/kernel/src/mem/alloc/buddy_system.c index 257dab10..1d53c592 100644 --- a/kernel/src/mem/alloc/buddy_system.c +++ b/kernel/src/mem/alloc/buddy_system.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[BUDDY ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/mem/alloc/heap.c b/kernel/src/mem/alloc/heap.c index eac1f759..ec017515 100644 --- a/kernel/src/mem/alloc/heap.c +++ b/kernel/src/mem/alloc/heap.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[KHEAP ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/mem/alloc/slab.c b/kernel/src/mem/alloc/slab.c index b4f55cc8..99801be5 100644 --- a/kernel/src/mem/alloc/slab.c +++ b/kernel/src/mem/alloc/slab.c @@ -9,7 +9,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[SLAB ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_INFO ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/mem/alloc/zone_allocator.c b/kernel/src/mem/alloc/zone_allocator.c index e20dabcf..f061373f 100644 --- a/kernel/src/mem/alloc/zone_allocator.c +++ b/kernel/src/mem/alloc/zone_allocator.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[PMM ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/mem/mm/mm.c b/kernel/src/mem/mm/mm.c index a66f510d..58ebf425 100644 --- a/kernel/src/mem/mm/mm.c +++ b/kernel/src/mem/mm/mm.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[MM_STR]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/slab.h" diff --git a/kernel/src/mem/mm/page.c b/kernel/src/mem/mm/page.c index 7bcb73b7..4ba97318 100644 --- a/kernel/src/mem/mm/page.c +++ b/kernel/src/mem/mm/page.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[PAGE ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/zone_allocator.h" diff --git a/kernel/src/mem/mm/vm_area.c b/kernel/src/mem/mm/vm_area.c index 32079455..461a978d 100644 --- a/kernel/src/mem/mm/vm_area.c +++ b/kernel/src/mem/mm/vm_area.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[VMA ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/mm/vm_area.h" diff --git a/kernel/src/mem/mm/vmem.c b/kernel/src/mem/mm/vmem.c index a41dc91c..848320c9 100644 --- a/kernel/src/mem/mm/vmem.c +++ b/kernel/src/mem/mm/vmem.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[VMEM ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/mm/vmem.h" diff --git a/kernel/src/mem/page_fault.c b/kernel/src/mem/page_fault.c index 8a61f0c7..f5b00c0b 100644 --- a/kernel/src/mem/page_fault.c +++ b/kernel/src/mem/page_fault.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[PG_FLT]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/page_fault.h" diff --git a/kernel/src/mem/paging.c b/kernel/src/mem/paging.c index c1be8e7b..92a806e3 100644 --- a/kernel/src/mem/paging.c +++ b/kernel/src/mem/paging.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[PAGING]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/multiboot.c b/kernel/src/multiboot.c index 36e5f820..61c57592 100644 --- a/kernel/src/multiboot.c +++ b/kernel/src/multiboot.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[MTBOOT]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "kernel.h" diff --git a/kernel/src/process/process.c b/kernel/src/process/process.c index 78edfcff..2b296695 100644 --- a/kernel/src/process/process.c +++ b/kernel/src/process/process.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[PROC ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/process/scheduler.c b/kernel/src/process/scheduler.c index 32eff37a..16e8a78a 100644 --- a/kernel/src/process/scheduler.c +++ b/kernel/src/process/scheduler.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[SCHED ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" @@ -137,18 +137,20 @@ void scheduler_run(pt_regs_t *f) #if 1 if (runqueue.curr->state == EXIT_ZOMBIE) { //==== Handle Zombies ================================================= - //pr_debug("Handle zombie %d\n", runqueue.curr->pid); - // get the next process after the current one - list_head_t *nNode = runqueue.curr->run_list.next; - // check if we reached the head of list_head_t - if (nNode == &runqueue.queue) { - nNode = nNode->next; - } - // get the task_struct - next = list_entry(nNode, task_struct, run_list); - // Remove the zombie task. + // Remove the zombie task first, so it cannot be selected again. scheduler_dequeue_task(runqueue.curr); - assert(next && "No valid task selected after removing ZOMBIE."); + + // Select the next runnable task from the runqueue. There can be + // transient windows where tasks exist but none are currently + // runnable yet (e.g., parent sleeping in waitpid and waiting to be + // awakened by signal delivery), so we idle-and-retry until one is. + next = scheduler_pick_next_task(&runqueue); + while (!next) { + sti(); + __asm__ __volatile__("hlt"); + cli(); + next = scheduler_pick_next_task(&runqueue); + } //===================================================================== } else { #endif @@ -176,8 +178,30 @@ void scheduler_run(pt_regs_t *f) next = scheduler_pick_next_task(&runqueue); } if (!next) { - // Still nothing, resume current (will likely block again). - next = runqueue.curr; + // Resume current only if it is still runnable and queued. + if ((runqueue.curr->state == TASK_RUNNING) && !list_head_empty(&runqueue.curr->run_list)) { + next = runqueue.curr; + } else { + // Last-resort attempt to find a runnable queued task. + list_for_each_decl(it, &runqueue.queue) + { + task_struct *entry = list_entry(it, task_struct, run_list); + if (entry->state == TASK_RUNNING) { + next = entry; + break; + } + } + } + + // If there is still no candidate, wait for an event and + // retry rather than panicking or returning to an invalid + // context. + while (!next) { + sti(); + __asm__ __volatile__("hlt"); + cli(); + next = scheduler_pick_next_task(&runqueue); + } } } //===================================================================== diff --git a/kernel/src/process/scheduler_algorithm.c b/kernel/src/process/scheduler_algorithm.c index fb475d13..400a518d 100644 --- a/kernel/src/process/scheduler_algorithm.c +++ b/kernel/src/process/scheduler_algorithm.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[SCHALG]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/process/wait.c b/kernel/src/process/wait.c index 14aa6a70..febaee35 100644 --- a/kernel/src/process/wait.c +++ b/kernel/src/process/wait.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[WAIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "process/wait.h" @@ -132,9 +132,11 @@ void wake_up_all(wait_queue_head_t *head) wait_queue_entry_t *entry = list_entry(it, wait_queue_entry_t, task_list); if (entry->func(entry, TASK_RUNNING, 0) || entry->task->state == TASK_RUNNING) { remove_wait_queue(head, entry); + // Clear the wait queue tracking field when removing from queue + entry->task->waiting_on = NULL; pr_debug("wake_up_all(%p): waking process %d\n", head, entry->task->pid); /* only enqueue if not already on runqueue */ - if (entry->task->run_list.next == &entry->task->run_list) { + if (list_head_empty(&entry->task->run_list)) { scheduler_enqueue_task(entry->task); } wait_queue_entry_dealloc(entry); @@ -214,26 +216,45 @@ wait_queue_entry_t *sleep_on(wait_queue_head_t *head) // inserting the entry; IRQs are restored before returning so normal operation resumes. uint8_t irqs = irq_disable(); - // Set the task state to uninterruptible to indicate it is sleeping. - sleeping_task->state = TASK_UNINTERRUPTIBLE; - - // Remove task from runqueue so scheduler will ignore it while blocked. - scheduler_dequeue_task(sleeping_task); - // If the process already has an entry on this queue, reuse it. list_for_each_decl(it, &head->task_list) { wait_queue_entry_t *e = list_entry(it, wait_queue_entry_t, task_list); if (e->task == sleeping_task) { + // Ensure a consistent sleeping state and keep runqueue counters + // unchanged if the task has already been dequeued. + sleeping_task->state = TASK_UNINTERRUPTIBLE; + if (!list_head_empty(&sleeping_task->run_list)) { + scheduler_dequeue_task(sleeping_task); + } + /* already registered, no allocation necessary */ irq_enable(irqs); return e; } } + // Set the task state to uninterruptible to indicate it is sleeping. + sleeping_task->state = TASK_UNINTERRUPTIBLE; + + // Track which wait queue this task is sleeping on. + sleeping_task->waiting_on = head; + + // Remove task from runqueue so scheduler will ignore it while blocked. + // Guard against double-dequeue: run_list is self-linked when not queued. + if (!list_head_empty(&sleeping_task->run_list)) { + scheduler_dequeue_task(sleeping_task); + } + // Allocate memory for a new wait queue entry. wait_queue_entry_t *entry = wait_queue_entry_alloc(); if (!entry) { pr_err("Failed to allocate memory for wait queue entry.\n"); + // Roll back sleeping state to avoid leaving the current process + // blocked and dequeued after allocation failure. + sleeping_task->state = TASK_RUNNING; + if (list_head_empty(&sleeping_task->run_list)) { + scheduler_enqueue_task(sleeping_task); + } irq_enable(irqs); return NULL; } diff --git a/kernel/src/resource_tracing.c b/kernel/src/resource_tracing.c index 9beeedfa..12dcc83d 100644 --- a/kernel/src/resource_tracing.c +++ b/kernel/src/resource_tracing.c @@ -8,7 +8,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[RESREG]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #define MAX_TRACKED_RESOURCES 1024 ///< Maximum number of tracked resources. diff --git a/kernel/src/sys/module.c b/kernel/src/sys/module.c index 00891c71..65a7f42e 100644 --- a/kernel/src/sys/module.c +++ b/kernel/src/sys/module.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[MODULE]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/slab.h" diff --git a/kernel/src/sys/utsname.c b/kernel/src/sys/utsname.c index fa5a2939..d91ed13d 100644 --- a/kernel/src/sys/utsname.c +++ b/kernel/src/sys/utsname.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[UTSNAM]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "errno.h" diff --git a/kernel/src/system/signal.c b/kernel/src/system/signal.c index 372b91f4..b6b3acce 100644 --- a/kernel/src/system/signal.c +++ b/kernel/src/system/signal.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[SIGNAL]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "system/signal.h" @@ -764,7 +764,57 @@ int sys_kill(pid_t pid, int sig) info.si_addr = NULL; info.si_status = 0; info.si_band = 0; - return __send_sig_info(sig, &info, process); + int ret = __send_sig_info(sig, &info, process); + + // If the target process is sleeping on a wait queue, remove it from the + // queue and re-enqueue it to the runqueue so the scheduler will run it + // and handle the signal. + if ((process->state == TASK_UNINTERRUPTIBLE || process->state == TASK_INTERRUPTIBLE) && + process->waiting_on != NULL) { + // The task is sleeping on a wait queue. We need to find and remove + // the entry from the wait queue, then move it to the runqueue. + wait_queue_head_t *wait_queue = process->waiting_on; + wait_queue_entry_t *entry = NULL; + + // Acquire the wait queue lock and search for the entry. + spinlock_lock(&wait_queue->lock); + list_for_each_decl(it, &wait_queue->task_list) { + wait_queue_entry_t *candidate = list_entry(it, wait_queue_entry_t, task_list); + if (candidate->task == process) { + entry = candidate; + break; + } + } + + // If we found the entry, remove it from the wait queue. + if (entry) { + list_head_remove(&entry->task_list); + process->waiting_on = NULL; + spinlock_unlock(&wait_queue->lock); + + // Now re-enqueue the task to the runqueue if not already queued. + process->state = TASK_RUNNING; + if (list_head_empty(&process->run_list)) { + scheduler_enqueue_task(process); + } + wait_queue_entry_dealloc(entry); + pr_debug("sys_kill: removed task %d from wait queue %p and re-enqueued\n", + process->pid, wait_queue); + } else { + spinlock_unlock(&wait_queue->lock); + pr_warning("sys_kill: task %d claims to sleep on wait queue %p but not found\n", + process->pid, wait_queue); + } + } else if (process->state == TASK_UNINTERRUPTIBLE || process->state == TASK_INTERRUPTIBLE) { + // Task is sleeping but not on a wait queue (shouldn't happen). + if (list_head_empty(&process->run_list)) { + process->state = TASK_RUNNING; + scheduler_enqueue_task(process); + pr_debug("sys_kill: re-enqueued sleeping task %d (not on wait queue)\n", process->pid); + } + } + + return ret; } sighandler_t sys_signal(int signum, sighandler_t handler, uint32_t sigreturn_addr) diff --git a/kernel/src/system/syscall.c b/kernel/src/system/syscall.c index ee9d2630..4a21a689 100644 --- a/kernel/src/system/syscall.c +++ b/kernel/src/system/syscall.c @@ -8,7 +8,7 @@ #include "sys/kernel_levels.h" // Include kernel log levels. #include "system/syscall_types.h" #define __DEBUG_HEADER__ "[SYSCLL]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "descriptor_tables/isr.h" diff --git a/kernel/src/tests/runner.c b/kernel/src/tests/runner.c index 1d6e94f2..21eea3a4 100644 --- a/kernel/src/tests/runner.c +++ b/kernel/src/tests/runner.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "tests/test.h" diff --git a/kernel/src/tests/unit/test_buddy.c b/kernel/src/tests/unit/test_buddy.c index 8e632255..4e1a5a5a 100644 --- a/kernel/src/tests/unit/test_buddy.c +++ b/kernel/src/tests/unit/test_buddy.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/buddy_system.h" diff --git a/kernel/src/tests/unit/test_dma.c b/kernel/src/tests/unit/test_dma.c index 3bf8d5ec..ae38496c 100644 --- a/kernel/src/tests/unit/test_dma.c +++ b/kernel/src/tests/unit/test_dma.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/slab.h" diff --git a/kernel/src/tests/unit/test_fpu.c b/kernel/src/tests/unit/test_fpu.c index 8def6b24..774df671 100644 --- a/kernel/src/tests/unit/test_fpu.c +++ b/kernel/src/tests/unit/test_fpu.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "devices/fpu.h" diff --git a/kernel/src/tests/unit/test_gdt.c b/kernel/src/tests/unit/test_gdt.c index a446cad6..2a44fae8 100644 --- a/kernel/src/tests/unit/test_gdt.c +++ b/kernel/src/tests/unit/test_gdt.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "descriptor_tables/gdt.h" diff --git a/kernel/src/tests/unit/test_idt.c b/kernel/src/tests/unit/test_idt.c index fea7d450..713c4631 100644 --- a/kernel/src/tests/unit/test_idt.c +++ b/kernel/src/tests/unit/test_idt.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "descriptor_tables/gdt.h" diff --git a/kernel/src/tests/unit/test_isr.c b/kernel/src/tests/unit/test_isr.c index 14153838..87eb4ead 100644 --- a/kernel/src/tests/unit/test_isr.c +++ b/kernel/src/tests/unit/test_isr.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "descriptor_tables/idt.h" diff --git a/kernel/src/tests/unit/test_memory_adversarial.c b/kernel/src/tests/unit/test_memory_adversarial.c index 8e65ec5b..980079bb 100644 --- a/kernel/src/tests/unit/test_memory_adversarial.c +++ b/kernel/src/tests/unit/test_memory_adversarial.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/slab.h" diff --git a/kernel/src/tests/unit/test_mm.c b/kernel/src/tests/unit/test_mm.c index 415371f4..159f33da 100644 --- a/kernel/src/tests/unit/test_mm.c +++ b/kernel/src/tests/unit/test_mm.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/zone_allocator.h" diff --git a/kernel/src/tests/unit/test_page.c b/kernel/src/tests/unit/test_page.c index 63e07ff9..5feae0f7 100644 --- a/kernel/src/tests/unit/test_page.c +++ b/kernel/src/tests/unit/test_page.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/zone_allocator.h" diff --git a/kernel/src/tests/unit/test_paging.c b/kernel/src/tests/unit/test_paging.c index e99c6a72..7e78621d 100644 --- a/kernel/src/tests/unit/test_paging.c +++ b/kernel/src/tests/unit/test_paging.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/mm/mm.h" diff --git a/kernel/src/tests/unit/test_scheduler.c b/kernel/src/tests/unit/test_scheduler.c index 2fb39e76..847db7aa 100644 --- a/kernel/src/tests/unit/test_scheduler.c +++ b/kernel/src/tests/unit/test_scheduler.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "process/scheduler.h" diff --git a/kernel/src/tests/unit/test_slab.c b/kernel/src/tests/unit/test_slab.c index cd20d48d..11a81bd3 100644 --- a/kernel/src/tests/unit/test_slab.c +++ b/kernel/src/tests/unit/test_slab.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/slab.h" diff --git a/kernel/src/tests/unit/test_vmem.c b/kernel/src/tests/unit/test_vmem.c index 15d79f3d..bf1ec2b0 100644 --- a/kernel/src/tests/unit/test_vmem.c +++ b/kernel/src/tests/unit/test_vmem.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/zone_allocator.h" diff --git a/kernel/src/tests/unit/test_zone_allocator.c b/kernel/src/tests/unit/test_zone_allocator.c index 8f99b0df..06c468c8 100644 --- a/kernel/src/tests/unit/test_zone_allocator.c +++ b/kernel/src/tests/unit/test_zone_allocator.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/zone_allocator.h" From 39f3932ce0dfa9fa3387fbe5ffeb2b04033896de Mon Sep 17 00:00:00 2001 From: "Enrico Fraccaroli (Galfurian)" Date: Mon, 2 Mar 2026 15:28:28 +0100 Subject: [PATCH 10/22] fix(scheduler): implement proper blocking in sys_waitpid The issue was that sys_waitpid() was returning 0 when no zombie child existed, causing userspace waitpid() to busy-wait in an infinite loop. When SIGCHLD arrived, the default signal handler just ignored it, so the busy-wait continued. Changes: - Add waitpid_queue wait queue for processes blocked in waitpid() - Modify sys_waitpid() to sleep on waitpid_queue when no zombie exists (unless WNOHANG) - Modify do_exit() to wake_up_all() processes sleeping in waitpid_queue - Return -EINTR from sys_waitpid after waking from signal - Fix userspace waitpid() to: * Properly check WNOHANG with bitwise & (was using logical &&) * Retry on -EINTR (signal interrupted the sleep) * Handle error cases correctly Now the shell properly blocks in waitpid() and is woken up when the child exits. --- kernel/src/process/scheduler.c | 29 ++++++++++++++++- lib/src/unistd/waitpid.c | 58 ++++++++++------------------------ qemu_test_output.txt | 8 +++++ 3 files changed, 53 insertions(+), 42 deletions(-) create mode 100644 qemu_test_output.txt diff --git a/kernel/src/process/scheduler.c b/kernel/src/process/scheduler.c index 16e8a78a..559a6da3 100644 --- a/kernel/src/process/scheduler.c +++ b/kernel/src/process/scheduler.c @@ -34,6 +34,9 @@ runqueue_t runqueue; // Definition of the global init process pointer task_struct *init_process = NULL; +/// Wait queue for processes blocked in waitpid(). +static wait_queue_head_t waitpid_queue; + void scheduler_initialize(void) { // Initialize the runqueue list of tasks. @@ -44,6 +47,8 @@ void scheduler_initialize(void) runqueue.curr = NULL; // Reset the number of active tasks. runqueue.num_active = 0; + // Initialize the waitpid wait queue. + wait_queue_head_init(&waitpid_queue); } task_struct *scheduler_get_current_process(void) { return runqueue.curr; } @@ -617,7 +622,26 @@ pid_t sys_waitpid(pid_t pid, int *status, int options) } // No eligible child process was found. - return 0; + // If WNOHANG is set, return immediately. + if (options & WNOHANG) { + return 0; + } + + // Otherwise, block until a child exits (and sends SIGCHLD to wake us up). + pr_debug("Process %d sleeping in waitpid (no zombie child yet)\n", runqueue.curr->pid); + wait_queue_entry_t *wait_entry = sleep_on(&waitpid_queue); + if (!wait_entry) { + pr_err("Failed to sleep in waitpid\n"); + return -ENOMEM; + } + + // After being woken up (by SIGCHLD), retry the search for zombie children. + // The signal handler will have already moved us back to the runqueue. + pr_debug("Process %d woken up from waitpid sleep\n", runqueue.curr->pid); + + // Return -EINTR to indicate the syscall was interrupted by a signal. + // The userspace waitpid() wrapper will retry the syscall. + return -EINTR; } void do_exit(int exit_code) @@ -643,6 +667,9 @@ void do_exit(int exit_code) } } + // Wake up any processes sleeping in waitpid(). + wake_up_all(&waitpid_queue); + // If it has children, then init process has to take care of them. if (!list_head_empty(&runqueue.curr->children)) { pr_debug( diff --git a/lib/src/unistd/waitpid.c b/lib/src/unistd/waitpid.c index f35e7724..27680d88 100644 --- a/lib/src/unistd/waitpid.c +++ b/lib/src/unistd/waitpid.c @@ -13,15 +13,29 @@ pid_t waitpid(pid_t pid, int *status, int options) { -#if 1 pid_t __res; int __status = 0; do { __inline_syscall_3(__res, waitpid, pid, &__status, options); - if (__res != 0) { + + // Success: A child process was reaped. + if (__res > 0) { break; } - if (options && WNOHANG) { + + // WNOHANG was set and no child changed state - return immediately. + if ((options & WNOHANG) && __res == 0) { + break; + } + + // If the syscall was interrupted by a signal (EINTR), retry it. + // This happens when we were sleeping in waitpid and SIGCHLD woke us up. + if (__res == -EINTR) { + continue; + } + + // Any other error (ECHILD, ESRCH, etc.) should be returned. + if (__res < 0) { break; } } while (1); @@ -30,44 +44,6 @@ pid_t waitpid(pid_t pid, int *status, int options) *status = __status; } __syscall_return(pid_t, __res); -#else - - pid_t ret; - - while (1) { - __inline_syscall_3(ret, waitpid, pid, status, options); - - // Success: A child process state has changed. - if (ret > 0) { - break; - } - - if (ret < 0) { - __syscall_set_errno(ret); - - // Interrupted by a signal: Retry the syscall. - if (errno == EINTR) { - continue; - } - - // No children to wait for, but WNOHANG allows non-blocking behavior. - if (errno == ECHILD) { - if (options & WNOHANG) { - // Return 0 to indicate no state change. - ret = 0; - break; - } - continue; - } - - // Unrecoverable error: Return -1 and let the caller handle `errno`. - break; - } - } - - // Return the PID of the child whose state has changed (or 0 for WNOHANG). - return ret; -#endif } pid_t wait(int *status) { return waitpid(-1, status, 0); } diff --git a/qemu_test_output.txt b/qemu_test_output.txt new file mode 100644 index 00000000..32b0bced --- /dev/null +++ b/qemu_test_output.txt @@ -0,0 +1,8 @@ +==================================================== +Starting QEMU test run (timeout: 120s)... +==================================================== +QEMU started with PID: 216157 +Monitoring test progress... +Build directory: build + +qemu-system-i386: -cdrom build/cdrom_test.iso: Could not open 'build/cdrom_test.iso': No such file or directory From 5c3ea7ea81f728c63611193697c9ca87bcee881f Mon Sep 17 00:00:00 2001 From: "Enrico Fraccaroli (Galfurian)" Date: Mon, 2 Mar 2026 15:50:53 +0100 Subject: [PATCH 11/22] fix(wait): remove problematic entry reuse logic in sleep_on The sleep_on() function had a path that would reuse an existing wait_queue_entry if the task was already on the queue. This caused issues because: 1. If the task was woken and the entry deallocated, stale pointers could remain 2. The check would return early without allocating a fresh entry 3. This could lead to the task not actually sleeping (early return) Solution: - Remove the entry-reuse optimization completely - Always allocate a fresh entry for each sleep_on() call - This ensures tasks actually sleep when added to the queue - Fixes the shell hanging after child process execution Also added missing clearing of waiting_on field when allocation fails. --- boot/src/multiboot.c | 2 +- kernel/src/crypt/sha256.c | 2 +- kernel/src/descriptor_tables/exception.c | 2 +- kernel/src/descriptor_tables/gdt.c | 2 +- kernel/src/descriptor_tables/idt.c | 2 +- kernel/src/descriptor_tables/interrupt.c | 2 +- kernel/src/descriptor_tables/tss.c | 2 +- kernel/src/devices/fpu.c | 2 +- kernel/src/devices/pci.c | 2 +- kernel/src/drivers/ata.c | 2 +- kernel/src/drivers/fdc.c | 2 +- kernel/src/drivers/keyboard/keyboard.c | 2 +- kernel/src/drivers/mem.c | 2 +- kernel/src/drivers/mouse.c | 2 +- kernel/src/drivers/ps2.c | 2 +- kernel/src/drivers/rtc.c | 2 +- kernel/src/elf/elf.c | 2 +- kernel/src/fs/ext2.c | 2 +- kernel/src/fs/namei.c | 2 +- kernel/src/fs/pipe.c | 2 +- kernel/src/fs/procfs.c | 2 +- kernel/src/fs/vfs.c | 2 +- kernel/src/hardware/pic8259.c | 2 +- kernel/src/hardware/timer.c | 2 +- kernel/src/io/proc_video.c | 2 +- kernel/src/io/video.c | 2 +- kernel/src/ipc/msg.c | 2 +- kernel/src/ipc/sem.c | 2 +- kernel/src/ipc/shm.c | 2 +- kernel/src/kernel.c | 2 +- kernel/src/klib/assert.c | 2 +- kernel/src/mem/alloc/buddy_system.c | 2 +- kernel/src/mem/alloc/heap.c | 2 +- kernel/src/mem/alloc/slab.c | 2 +- kernel/src/mem/alloc/zone_allocator.c | 2 +- kernel/src/mem/mm/mm.c | 2 +- kernel/src/mem/mm/page.c | 2 +- kernel/src/mem/mm/vm_area.c | 2 +- kernel/src/mem/mm/vmem.c | 2 +- kernel/src/mem/page_fault.c | 2 +- kernel/src/mem/paging.c | 2 +- kernel/src/multiboot.c | 2 +- kernel/src/process/wait.c | 18 +----------------- kernel/src/resource_tracing.c | 2 +- kernel/src/sys/module.c | 2 +- kernel/src/sys/utsname.c | 2 +- kernel/src/system/signal.c | 2 +- kernel/src/system/syscall.c | 2 +- kernel/src/tests/runner.c | 2 +- kernel/src/tests/unit/test_buddy.c | 2 +- kernel/src/tests/unit/test_dma.c | 2 +- kernel/src/tests/unit/test_fpu.c | 2 +- kernel/src/tests/unit/test_gdt.c | 2 +- kernel/src/tests/unit/test_idt.c | 2 +- kernel/src/tests/unit/test_isr.c | 2 +- .../src/tests/unit/test_memory_adversarial.c | 2 +- kernel/src/tests/unit/test_mm.c | 2 +- kernel/src/tests/unit/test_page.c | 2 +- kernel/src/tests/unit/test_paging.c | 2 +- kernel/src/tests/unit/test_scheduler.c | 2 +- kernel/src/tests/unit/test_slab.c | 2 +- kernel/src/tests/unit/test_vmem.c | 2 +- kernel/src/tests/unit/test_zone_allocator.c | 2 +- 63 files changed, 63 insertions(+), 79 deletions(-) diff --git a/boot/src/multiboot.c b/boot/src/multiboot.c index 61c57592..36e5f820 100644 --- a/boot/src/multiboot.c +++ b/boot/src/multiboot.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[MTBOOT]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "kernel.h" diff --git a/kernel/src/crypt/sha256.c b/kernel/src/crypt/sha256.c index d8d6397e..913fdc96 100644 --- a/kernel/src/crypt/sha256.c +++ b/kernel/src/crypt/sha256.c @@ -13,7 +13,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[SHA256]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "crypt/sha256.h" diff --git a/kernel/src/descriptor_tables/exception.c b/kernel/src/descriptor_tables/exception.c index 5000ca4f..1f8b07e5 100644 --- a/kernel/src/descriptor_tables/exception.c +++ b/kernel/src/descriptor_tables/exception.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[EXEPT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "descriptor_tables/idt.h" diff --git a/kernel/src/descriptor_tables/gdt.c b/kernel/src/descriptor_tables/gdt.c index a66ce75a..9bc8166a 100644 --- a/kernel/src/descriptor_tables/gdt.c +++ b/kernel/src/descriptor_tables/gdt.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[GDT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "descriptor_tables/gdt.h" diff --git a/kernel/src/descriptor_tables/idt.c b/kernel/src/descriptor_tables/idt.c index 09673d4c..d64557cd 100644 --- a/kernel/src/descriptor_tables/idt.c +++ b/kernel/src/descriptor_tables/idt.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[IDT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "descriptor_tables/gdt.h" diff --git a/kernel/src/descriptor_tables/interrupt.c b/kernel/src/descriptor_tables/interrupt.c index eb332a8a..55662610 100644 --- a/kernel/src/descriptor_tables/interrupt.c +++ b/kernel/src/descriptor_tables/interrupt.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[IRQ ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/descriptor_tables/tss.c b/kernel/src/descriptor_tables/tss.c index a41a7c84..022c210a 100644 --- a/kernel/src/descriptor_tables/tss.c +++ b/kernel/src/descriptor_tables/tss.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TSS ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "descriptor_tables/gdt.h" diff --git a/kernel/src/devices/fpu.c b/kernel/src/devices/fpu.c index 3227ed17..e7588484 100644 --- a/kernel/src/devices/fpu.c +++ b/kernel/src/devices/fpu.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[FPU ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/devices/pci.c b/kernel/src/devices/pci.c index 75f6a83f..95e5277e 100644 --- a/kernel/src/devices/pci.c +++ b/kernel/src/devices/pci.c @@ -7,7 +7,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[PCI ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "devices/pci.h" diff --git a/kernel/src/drivers/ata.c b/kernel/src/drivers/ata.c index 2212538f..a728af6f 100644 --- a/kernel/src/drivers/ata.c +++ b/kernel/src/drivers/ata.c @@ -8,7 +8,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[ATA ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "drivers/ata/ata.h" diff --git a/kernel/src/drivers/fdc.c b/kernel/src/drivers/fdc.c index 81ae98c3..c2829dd1 100644 --- a/kernel/src/drivers/fdc.c +++ b/kernel/src/drivers/fdc.c @@ -8,7 +8,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[FDC ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "drivers/fdc.h" diff --git a/kernel/src/drivers/keyboard/keyboard.c b/kernel/src/drivers/keyboard/keyboard.c index c9a2d1c6..08f5d4b8 100644 --- a/kernel/src/drivers/keyboard/keyboard.c +++ b/kernel/src/drivers/keyboard/keyboard.c @@ -8,7 +8,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[KEYBRD]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "ctype.h" diff --git a/kernel/src/drivers/mem.c b/kernel/src/drivers/mem.c index b49f918f..1b316f65 100644 --- a/kernel/src/drivers/mem.c +++ b/kernel/src/drivers/mem.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[MEMDEV]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/drivers/mouse.c b/kernel/src/drivers/mouse.c index f227e214..533c33a9 100644 --- a/kernel/src/drivers/mouse.c +++ b/kernel/src/drivers/mouse.c @@ -8,7 +8,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[MOUSE ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "descriptor_tables/isr.h" diff --git a/kernel/src/drivers/ps2.c b/kernel/src/drivers/ps2.c index 43f79ac7..fb9e642d 100644 --- a/kernel/src/drivers/ps2.c +++ b/kernel/src/drivers/ps2.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[PS/2 ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "drivers/ps2.h" diff --git a/kernel/src/drivers/rtc.c b/kernel/src/drivers/rtc.c index cf5aa29d..6ad21dc8 100644 --- a/kernel/src/drivers/rtc.c +++ b/kernel/src/drivers/rtc.c @@ -8,7 +8,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[RTC ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "descriptor_tables/isr.h" diff --git a/kernel/src/elf/elf.c b/kernel/src/elf/elf.c index 706c938a..5848f6eb 100644 --- a/kernel/src/elf/elf.c +++ b/kernel/src/elf/elf.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[ELF ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/fs/ext2.c b/kernel/src/fs/ext2.c index 18728009..f21020bd 100644 --- a/kernel/src/fs/ext2.c +++ b/kernel/src/fs/ext2.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[EXT2 ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. // If defined, ETX2 will debug everything. // #define EXT2_FULL_DEBUG diff --git a/kernel/src/fs/namei.c b/kernel/src/fs/namei.c index 2821442d..4c22c534 100644 --- a/kernel/src/fs/namei.c +++ b/kernel/src/fs/namei.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[NAMEI ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/fs/pipe.c b/kernel/src/fs/pipe.c index 0136e8f2..93b37749 100644 --- a/kernel/src/fs/pipe.c +++ b/kernel/src/fs/pipe.c @@ -12,7 +12,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[PIPE ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. // ============================================================================ diff --git a/kernel/src/fs/procfs.c b/kernel/src/fs/procfs.c index 326c0fd9..3e208501 100644 --- a/kernel/src/fs/procfs.c +++ b/kernel/src/fs/procfs.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[PROCFS]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/fs/vfs.c b/kernel/src/fs/vfs.c index fa272071..a5e07746 100644 --- a/kernel/src/fs/vfs.c +++ b/kernel/src/fs/vfs.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[VFS ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/hardware/pic8259.c b/kernel/src/hardware/pic8259.c index c4e69c32..abd98b00 100644 --- a/kernel/src/hardware/pic8259.c +++ b/kernel/src/hardware/pic8259.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[PIC ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "hardware/pic8259.h" diff --git a/kernel/src/hardware/timer.c b/kernel/src/hardware/timer.c index 1ebb73b3..c9ac1a36 100644 --- a/kernel/src/hardware/timer.c +++ b/kernel/src/hardware/timer.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TIMER ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/io/proc_video.c b/kernel/src/io/proc_video.c index 0c5a4b7f..2e11c4e6 100644 --- a/kernel/src/io/proc_video.c +++ b/kernel/src/io/proc_video.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[PROCV ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "bits/ioctls.h" diff --git a/kernel/src/io/video.c b/kernel/src/io/video.c index c7c347dd..1571258c 100644 --- a/kernel/src/io/video.c +++ b/kernel/src/io/video.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" #define __DEBUG_HEADER__ "[VIDEO ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" #include "ctype.h" diff --git a/kernel/src/ipc/msg.c b/kernel/src/ipc/msg.c index 3abcc719..89ccdf6f 100644 --- a/kernel/src/ipc/msg.c +++ b/kernel/src/ipc/msg.c @@ -7,7 +7,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[IPCmsg]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. // ============================================================================ diff --git a/kernel/src/ipc/sem.c b/kernel/src/ipc/sem.c index 326aa32e..47a32762 100644 --- a/kernel/src/ipc/sem.c +++ b/kernel/src/ipc/sem.c @@ -34,7 +34,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[IPCsem]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. // ============================================================================ diff --git a/kernel/src/ipc/shm.c b/kernel/src/ipc/shm.c index 3f6d7b15..3e3e2654 100644 --- a/kernel/src/ipc/shm.c +++ b/kernel/src/ipc/shm.c @@ -7,7 +7,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[IPCshm]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. // ============================================================================ diff --git a/kernel/src/kernel.c b/kernel/src/kernel.c index bcea0e82..6feb4146 100644 --- a/kernel/src/kernel.c +++ b/kernel/src/kernel.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[KERNEL]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "kernel.h" diff --git a/kernel/src/klib/assert.c b/kernel/src/klib/assert.c index c1637230..98833f3b 100644 --- a/kernel/src/klib/assert.c +++ b/kernel/src/klib/assert.c @@ -10,7 +10,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[ASSERT]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. void __assert_fail(const char *assertion, const char *file, const char *function, unsigned int line) diff --git a/kernel/src/mem/alloc/buddy_system.c b/kernel/src/mem/alloc/buddy_system.c index 1d53c592..257dab10 100644 --- a/kernel/src/mem/alloc/buddy_system.c +++ b/kernel/src/mem/alloc/buddy_system.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[BUDDY ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/mem/alloc/heap.c b/kernel/src/mem/alloc/heap.c index ec017515..eac1f759 100644 --- a/kernel/src/mem/alloc/heap.c +++ b/kernel/src/mem/alloc/heap.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[KHEAP ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/mem/alloc/slab.c b/kernel/src/mem/alloc/slab.c index 99801be5..b4f55cc8 100644 --- a/kernel/src/mem/alloc/slab.c +++ b/kernel/src/mem/alloc/slab.c @@ -9,7 +9,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[SLAB ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_INFO ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/mem/alloc/zone_allocator.c b/kernel/src/mem/alloc/zone_allocator.c index f061373f..e20dabcf 100644 --- a/kernel/src/mem/alloc/zone_allocator.c +++ b/kernel/src/mem/alloc/zone_allocator.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[PMM ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/mem/mm/mm.c b/kernel/src/mem/mm/mm.c index 58ebf425..a66f510d 100644 --- a/kernel/src/mem/mm/mm.c +++ b/kernel/src/mem/mm/mm.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[MM_STR]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/slab.h" diff --git a/kernel/src/mem/mm/page.c b/kernel/src/mem/mm/page.c index 4ba97318..7bcb73b7 100644 --- a/kernel/src/mem/mm/page.c +++ b/kernel/src/mem/mm/page.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[PAGE ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/zone_allocator.h" diff --git a/kernel/src/mem/mm/vm_area.c b/kernel/src/mem/mm/vm_area.c index 461a978d..32079455 100644 --- a/kernel/src/mem/mm/vm_area.c +++ b/kernel/src/mem/mm/vm_area.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[VMA ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/mm/vm_area.h" diff --git a/kernel/src/mem/mm/vmem.c b/kernel/src/mem/mm/vmem.c index 848320c9..a41dc91c 100644 --- a/kernel/src/mem/mm/vmem.c +++ b/kernel/src/mem/mm/vmem.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[VMEM ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/mm/vmem.h" diff --git a/kernel/src/mem/page_fault.c b/kernel/src/mem/page_fault.c index f5b00c0b..8a61f0c7 100644 --- a/kernel/src/mem/page_fault.c +++ b/kernel/src/mem/page_fault.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[PG_FLT]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/page_fault.h" diff --git a/kernel/src/mem/paging.c b/kernel/src/mem/paging.c index 92a806e3..c1be8e7b 100644 --- a/kernel/src/mem/paging.c +++ b/kernel/src/mem/paging.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[PAGING]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/multiboot.c b/kernel/src/multiboot.c index 61c57592..36e5f820 100644 --- a/kernel/src/multiboot.c +++ b/kernel/src/multiboot.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[MTBOOT]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "kernel.h" diff --git a/kernel/src/process/wait.c b/kernel/src/process/wait.c index febaee35..6b196643 100644 --- a/kernel/src/process/wait.c +++ b/kernel/src/process/wait.c @@ -216,23 +216,6 @@ wait_queue_entry_t *sleep_on(wait_queue_head_t *head) // inserting the entry; IRQs are restored before returning so normal operation resumes. uint8_t irqs = irq_disable(); - // If the process already has an entry on this queue, reuse it. - list_for_each_decl(it, &head->task_list) { - wait_queue_entry_t *e = list_entry(it, wait_queue_entry_t, task_list); - if (e->task == sleeping_task) { - // Ensure a consistent sleeping state and keep runqueue counters - // unchanged if the task has already been dequeued. - sleeping_task->state = TASK_UNINTERRUPTIBLE; - if (!list_head_empty(&sleeping_task->run_list)) { - scheduler_dequeue_task(sleeping_task); - } - - /* already registered, no allocation necessary */ - irq_enable(irqs); - return e; - } - } - // Set the task state to uninterruptible to indicate it is sleeping. sleeping_task->state = TASK_UNINTERRUPTIBLE; @@ -252,6 +235,7 @@ wait_queue_entry_t *sleep_on(wait_queue_head_t *head) // Roll back sleeping state to avoid leaving the current process // blocked and dequeued after allocation failure. sleeping_task->state = TASK_RUNNING; + sleeping_task->waiting_on = NULL; if (list_head_empty(&sleeping_task->run_list)) { scheduler_enqueue_task(sleeping_task); } diff --git a/kernel/src/resource_tracing.c b/kernel/src/resource_tracing.c index 12dcc83d..9beeedfa 100644 --- a/kernel/src/resource_tracing.c +++ b/kernel/src/resource_tracing.c @@ -8,7 +8,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[RESREG]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #define MAX_TRACKED_RESOURCES 1024 ///< Maximum number of tracked resources. diff --git a/kernel/src/sys/module.c b/kernel/src/sys/module.c index 65a7f42e..00891c71 100644 --- a/kernel/src/sys/module.c +++ b/kernel/src/sys/module.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[MODULE]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/slab.h" diff --git a/kernel/src/sys/utsname.c b/kernel/src/sys/utsname.c index d91ed13d..fa5a2939 100644 --- a/kernel/src/sys/utsname.c +++ b/kernel/src/sys/utsname.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[UTSNAM]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "errno.h" diff --git a/kernel/src/system/signal.c b/kernel/src/system/signal.c index b6b3acce..48fe6291 100644 --- a/kernel/src/system/signal.c +++ b/kernel/src/system/signal.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[SIGNAL]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "system/signal.h" diff --git a/kernel/src/system/syscall.c b/kernel/src/system/syscall.c index 4a21a689..ee9d2630 100644 --- a/kernel/src/system/syscall.c +++ b/kernel/src/system/syscall.c @@ -8,7 +8,7 @@ #include "sys/kernel_levels.h" // Include kernel log levels. #include "system/syscall_types.h" #define __DEBUG_HEADER__ "[SYSCLL]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "descriptor_tables/isr.h" diff --git a/kernel/src/tests/runner.c b/kernel/src/tests/runner.c index 21eea3a4..1d6e94f2 100644 --- a/kernel/src/tests/runner.c +++ b/kernel/src/tests/runner.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "tests/test.h" diff --git a/kernel/src/tests/unit/test_buddy.c b/kernel/src/tests/unit/test_buddy.c index 4e1a5a5a..8e632255 100644 --- a/kernel/src/tests/unit/test_buddy.c +++ b/kernel/src/tests/unit/test_buddy.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/buddy_system.h" diff --git a/kernel/src/tests/unit/test_dma.c b/kernel/src/tests/unit/test_dma.c index ae38496c..3bf8d5ec 100644 --- a/kernel/src/tests/unit/test_dma.c +++ b/kernel/src/tests/unit/test_dma.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/slab.h" diff --git a/kernel/src/tests/unit/test_fpu.c b/kernel/src/tests/unit/test_fpu.c index 774df671..8def6b24 100644 --- a/kernel/src/tests/unit/test_fpu.c +++ b/kernel/src/tests/unit/test_fpu.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "devices/fpu.h" diff --git a/kernel/src/tests/unit/test_gdt.c b/kernel/src/tests/unit/test_gdt.c index 2a44fae8..a446cad6 100644 --- a/kernel/src/tests/unit/test_gdt.c +++ b/kernel/src/tests/unit/test_gdt.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "descriptor_tables/gdt.h" diff --git a/kernel/src/tests/unit/test_idt.c b/kernel/src/tests/unit/test_idt.c index 713c4631..fea7d450 100644 --- a/kernel/src/tests/unit/test_idt.c +++ b/kernel/src/tests/unit/test_idt.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "descriptor_tables/gdt.h" diff --git a/kernel/src/tests/unit/test_isr.c b/kernel/src/tests/unit/test_isr.c index 87eb4ead..14153838 100644 --- a/kernel/src/tests/unit/test_isr.c +++ b/kernel/src/tests/unit/test_isr.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "descriptor_tables/idt.h" diff --git a/kernel/src/tests/unit/test_memory_adversarial.c b/kernel/src/tests/unit/test_memory_adversarial.c index 980079bb..8e65ec5b 100644 --- a/kernel/src/tests/unit/test_memory_adversarial.c +++ b/kernel/src/tests/unit/test_memory_adversarial.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/slab.h" diff --git a/kernel/src/tests/unit/test_mm.c b/kernel/src/tests/unit/test_mm.c index 159f33da..415371f4 100644 --- a/kernel/src/tests/unit/test_mm.c +++ b/kernel/src/tests/unit/test_mm.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/zone_allocator.h" diff --git a/kernel/src/tests/unit/test_page.c b/kernel/src/tests/unit/test_page.c index 5feae0f7..63e07ff9 100644 --- a/kernel/src/tests/unit/test_page.c +++ b/kernel/src/tests/unit/test_page.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/zone_allocator.h" diff --git a/kernel/src/tests/unit/test_paging.c b/kernel/src/tests/unit/test_paging.c index 7e78621d..e99c6a72 100644 --- a/kernel/src/tests/unit/test_paging.c +++ b/kernel/src/tests/unit/test_paging.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/mm/mm.h" diff --git a/kernel/src/tests/unit/test_scheduler.c b/kernel/src/tests/unit/test_scheduler.c index 847db7aa..2fb39e76 100644 --- a/kernel/src/tests/unit/test_scheduler.c +++ b/kernel/src/tests/unit/test_scheduler.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "process/scheduler.h" diff --git a/kernel/src/tests/unit/test_slab.c b/kernel/src/tests/unit/test_slab.c index 11a81bd3..cd20d48d 100644 --- a/kernel/src/tests/unit/test_slab.c +++ b/kernel/src/tests/unit/test_slab.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/slab.h" diff --git a/kernel/src/tests/unit/test_vmem.c b/kernel/src/tests/unit/test_vmem.c index bf1ec2b0..15d79f3d 100644 --- a/kernel/src/tests/unit/test_vmem.c +++ b/kernel/src/tests/unit/test_vmem.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/zone_allocator.h" diff --git a/kernel/src/tests/unit/test_zone_allocator.c b/kernel/src/tests/unit/test_zone_allocator.c index 06c468c8..8f99b0df 100644 --- a/kernel/src/tests/unit/test_zone_allocator.c +++ b/kernel/src/tests/unit/test_zone_allocator.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/zone_allocator.h" From 09dd96d2e616d3b299f55272e1c707862984bf16 Mon Sep 17 00:00:00 2001 From: Galfurian Date: Tue, 3 Mar 2026 16:03:47 +0100 Subject: [PATCH 12/22] fix(process): correct wait/wakeup queue handling and scheduler interactions --- kernel/inc/klib/spinlock.h | 2 + kernel/inc/process/wait.h | 13 +- kernel/src/drivers/keyboard/keyboard.c | 8 +- kernel/src/hardware/timer.c | 14 ++- kernel/src/process/process.c | 1 + kernel/src/process/scheduler.c | 165 ++++++++++++++----------- kernel/src/process/wait.c | 86 +++++++++---- kernel/src/system/signal.c | 18 +-- lib/inc/list_head.h | 13 +- userspace/bin/init.c | 7 +- 10 files changed, 196 insertions(+), 131 deletions(-) diff --git a/kernel/inc/klib/spinlock.h b/kernel/inc/klib/spinlock.h index 6565f140..6d04755f 100644 --- a/kernel/inc/klib/spinlock.h +++ b/kernel/inc/klib/spinlock.h @@ -11,6 +11,8 @@ #define SPINLOCK_FREE 0 /// Determines if the spinlock is busy. #define SPINLOCK_BUSY 1 +/// Initializes a spinlock with the value of SPINLOCK_FREE. +#define SPINLOCK_INIT SPINLOCK_FREE /// @brief Spinlock structure. typedef atomic_t spinlock_t; diff --git a/kernel/inc/process/wait.h b/kernel/inc/process/wait.h index 1111b6b3..8388c481 100644 --- a/kernel/inc/process/wait.h +++ b/kernel/inc/process/wait.h @@ -61,10 +61,9 @@ /// @brief Head of the waiting queue. typedef struct wait_queue_head { - /// Locking element for the waiting queque. - spinlock_t lock; - /// Head of the waiting queue, it contains wait_queue_entry_t elements. - struct list_head task_list; + const char *name; ///< Name of the wait queue (Added for debug purpose). + spinlock_t lock; ///< Locking element for the waiting queque. + struct list_head task_list; ///< Head of the waiting queue, it contains wait_queue_entry_t elements. } wait_queue_head_t; /// @brief Entry of the waiting queue. @@ -130,3 +129,9 @@ wait_queue_entry_t *sleep_on(wait_queue_head_t *head); /// returns non‑zero. This is the generic helper used by drivers when /// data arrives. void wake_up_all(wait_queue_head_t *head); + +/// @brief Wake up a specific process if it is waiting on the given queue. +/// @param head The wait queue head to search. +/// @param task The specific task to wake up. +/// @return 1 if the task was found and woken up, 0 otherwise. +int wake_up_process_on_queue(wait_queue_head_t *head, struct task_struct *task); diff --git a/kernel/src/drivers/keyboard/keyboard.c b/kernel/src/drivers/keyboard/keyboard.c index 08f5d4b8..f06d28ae 100644 --- a/kernel/src/drivers/keyboard/keyboard.c +++ b/kernel/src/drivers/keyboard/keyboard.c @@ -37,7 +37,11 @@ spinlock_t scancodes_lock; /* wait queue used by keyboard_pop_back(), initialized in * keyboard_initialize(). Readers sleep here when the scancode buffer * is empty; the ISR wakes them when a new key arrives. */ -static wait_queue_head_t keyboard_wait_queue; +static wait_queue_head_t keyboard_wait_queue = { + .name = "keyboard_wait_queue", + .lock = SPINLOCK_INIT, + .task_list = LIST_HEAD_INIT(keyboard_wait_queue.task_list), +}; #define KBD_LEFT_SHIFT (1 << 0) ///< Flag which identifies the left shift. #define KBD_RIGHT_SHIFT (1 << 1) ///< Flag which identifies the right shift. @@ -433,8 +437,6 @@ int keyboard_initialize(void) rb_keybuffer_init(&scancodes); // Initialize the spinlock. spinlock_init(&scancodes_lock); - // init wait queue used by readers - wait_queue_head_init(&keyboard_wait_queue); // Initialize the keymaps. init_keymaps(); // Install the IRQ. diff --git a/kernel/src/hardware/timer.c b/kernel/src/hardware/timer.c index c9ac1a36..971feb8d 100644 --- a/kernel/src/hardware/timer.c +++ b/kernel/src/hardware/timer.c @@ -4,10 +4,10 @@ /// See LICENSE.md for details. // Setup the logging for this file (do this before any other include). -#include "sys/kernel_levels.h" // Include kernel log levels. -#define __DEBUG_HEADER__ "[TIMER ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. -#include "io/debug.h" // Include debugging functions. +#include "sys/kernel_levels.h" // Include kernel log levels. +#define __DEBUG_HEADER__ "[TIMER ]" ///< Change header. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#include "io/debug.h" // Include debugging functions. #include "assert.h" #include "descriptor_tables/isr.h" @@ -75,7 +75,11 @@ static __volatile__ unsigned long timer_ticks = 0; /// Contains timer for each CPU (for now only one) static tvec_base_t cpu_base = {0}; /// Contains all process waiting for a sleep. -static wait_queue_head_t sleep_queue; +static wait_queue_head_t sleep_queue = { + .name = "sleep_queue", + .lock = SPINLOCK_INIT, + .task_list = LIST_HEAD_INIT(sleep_queue.task_list), +}; void timer_phase(const uint32_t hz) { diff --git a/kernel/src/process/process.c b/kernel/src/process/process.c index 2b296695..5c041557 100644 --- a/kernel/src/process/process.c +++ b/kernel/src/process/process.c @@ -703,5 +703,6 @@ int sys_execve(pt_regs_t *f) scheduler_restore_context(current, f); pr_debug("Executing '%s' (pid: %d)...\n", current->name, current->pid); + return 0; } diff --git a/kernel/src/process/scheduler.c b/kernel/src/process/scheduler.c index 559a6da3..a548949c 100644 --- a/kernel/src/process/scheduler.c +++ b/kernel/src/process/scheduler.c @@ -4,10 +4,10 @@ /// See LICENSE.md for details. // Setup the logging for this file (do this before any other include). -#include "sys/kernel_levels.h" // Include kernel log levels. -#define __DEBUG_HEADER__ "[SCHED ]" ///< Change header. +#include "sys/kernel_levels.h" // Include kernel log levels. +#define __DEBUG_HEADER__ "[SCHED ]" ///< Change header. #define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. -#include "io/debug.h" // Include debugging functions. +#include "io/debug.h" // Include debugging functions. #include "assert.h" #include "descriptor_tables/tss.h" @@ -35,7 +35,11 @@ runqueue_t runqueue; task_struct *init_process = NULL; /// Wait queue for processes blocked in waitpid(). -static wait_queue_head_t waitpid_queue; +static wait_queue_head_t waitpid_queue = { + .name = "waitpid_queue", + .lock = SPINLOCK_INIT, + .task_list = LIST_HEAD_INIT(waitpid_queue.task_list), +}; void scheduler_initialize(void) { @@ -103,6 +107,14 @@ void scheduler_enqueue_task(task_struct *process) // Increment the number of active processes. ++runqueue.num_active; + // Print the entire running queue for debugging purposes. + pr_debug("scheduler_enqueue_task(%d): runqueue: [", process->pid); + list_for_each_decl (running_it, &runqueue.queue) { + task_struct *running_task = list_entry(running_it, task_struct, run_list); + pr_debug("%d ", running_task->pid); + } + pr_debug("]\n"); + #ifdef ENABLE_SCHEDULER_FEEDBACK scheduler_feedback_task_add(process); #endif @@ -119,6 +131,14 @@ void scheduler_dequeue_task(task_struct *process) runqueue.num_periodic--; } + // Print the entire running queue for debugging purposes. + pr_debug("scheduler_dequeue_task(%d): runqueue: [", process->pid); + list_for_each_decl (running_it, &runqueue.queue) { + task_struct *running_task = list_entry(running_it, task_struct, run_list); + pr_debug("%d ", running_task->pid); + } + pr_debug("]\n"); + #ifdef ENABLE_SCHEDULER_FEEDBACK scheduler_feedback_task_remove(process->pid); #endif @@ -139,83 +159,75 @@ void scheduler_run(pt_regs_t *f) // We check the existence of pending signals every time we finish // handling an interrupt or an exception. if (!do_signal(f)) { -#if 1 + if (runqueue.curr->state == EXIT_ZOMBIE) { - //==== Handle Zombies ================================================= + + pr_debug("SCHEDULER_RUN: Current task %d is a zombie. Removing it and picking next task.\n", runqueue.curr->pid); + // Remove the zombie task first, so it cannot be selected again. scheduler_dequeue_task(runqueue.curr); + } - // Select the next runnable task from the runqueue. There can be - // transient windows where tasks exist but none are currently - // runnable yet (e.g., parent sleeping in waitpid and waiting to be - // awakened by signal delivery), so we idle-and-retry until one is. - next = scheduler_pick_next_task(&runqueue); - while (!next) { - sti(); - __asm__ __volatile__("hlt"); - cli(); - next = scheduler_pick_next_task(&runqueue); - } - //===================================================================== - } else { -#endif - //==== Scheduling ===================================================== - // If we are currently executing a periodic process, and this process - // has yet to complete, keep executing it. + // If we are currently executing a periodic process, and this process + // has yet to complete, keep executing it. #ifdef SCHEDULER_EDF - if (runqueue.curr->se.is_periodic) - if (!runqueue.curr->se.executed) - return; + if (runqueue.curr->se.is_periodic) + if (!runqueue.curr->se.executed) + return; #endif - // Pointer to the next process to be executed. - next = scheduler_pick_next_task(&runqueue); - - // If no runnable task was found we may be in a situation where every user process is blocked. rather than - // assert or return to the blocked task we idle the cpu until some other task is enqueued; the - // scheduler_pick_next_task call above will have already re-enabled the cpu if necessary. - if (!next) { - if (runqueue.num_active == 0) { - while (runqueue.num_active == 0) { - sti(); - __asm__ __volatile__("hlt"); - cli(); - } - next = scheduler_pick_next_task(&runqueue); + // Pointer to the next process to be executed. + next = scheduler_pick_next_task(&runqueue); + + // If no runnable task was found we may be in a situation where every user process is blocked. rather than + // assert or return to the blocked task we idle the cpu until some other task is enqueued; the + // scheduler_pick_next_task call above will have already re-enabled the cpu if necessary. + if (!next) { + if (runqueue.num_active == 0) { + while (runqueue.num_active == 0) { + sti(); + __asm__ __volatile__("hlt"); + cli(); } - if (!next) { - // Resume current only if it is still runnable and queued. - if ((runqueue.curr->state == TASK_RUNNING) && !list_head_empty(&runqueue.curr->run_list)) { - next = runqueue.curr; - } else { - // Last-resort attempt to find a runnable queued task. - list_for_each_decl(it, &runqueue.queue) - { - task_struct *entry = list_entry(it, task_struct, run_list); - if (entry->state == TASK_RUNNING) { - next = entry; - break; - } + next = scheduler_pick_next_task(&runqueue); + pr_debug("SCHEDULER_RUN: No active tasks, idling until a task is enqueued. Next task: %d\n", next ? next->pid : -1); + } + if (!next) { + // Resume current only if it is still runnable and queued. + if ((runqueue.curr->state == TASK_RUNNING) && !list_head_empty(&runqueue.curr->run_list)) { + next = runqueue.curr; + } else { + // Last-resort attempt to find a runnable queued task. + list_for_each_decl (it, &runqueue.queue) { + task_struct *entry = list_entry(it, task_struct, run_list); + if (entry->state == TASK_RUNNING) { + next = entry; + pr_debug("SCHEDULER_RUN: No runnable tasks found, but found queued task %d in state TASK_RUNNING. Resuming it.\n", entry->pid); + break; } } + } - // If there is still no candidate, wait for an event and - // retry rather than panicking or returning to an invalid - // context. - while (!next) { - sti(); - __asm__ __volatile__("hlt"); - cli(); - next = scheduler_pick_next_task(&runqueue); - } + // If there is still no candidate, wait for an event and + // retry rather than panicking or returning to an invalid + // context. + while (!next) { + sti(); + __asm__ __volatile__("hlt"); + cli(); + next = scheduler_pick_next_task(&runqueue); } + pr_debug("SCHEDULER_RUN: No runnable tasks, idling until a task is enqueued. Next task: %d\n", next ? next->pid : -1); } - //===================================================================== } + // Check if the next and current processes are different. if (next != runqueue.curr) { + pr_debug("SCHEDULER_RUN: Picked next task %d to run.\n", next->pid); // Copy into Kernel stack the next process's context. scheduler_restore_context(next, f); } + } else { + pr_warning("SCHEDULER_RUN: Signal handler requested context switch, but signal handling is not yet implemented. Ignoring.\n"); } //========================================================================== } @@ -397,7 +409,7 @@ int sys_setpgid(pid_t pid, pid_t pgid) // Set the new process group ID. task->pgid = pgid; - pr_debug("Process %d assigned to process group %d.", task->pid, pgid); + pr_debug("Process %d assigned to process group %d.\n", task->pid, pgid); return 0; } @@ -615,7 +627,7 @@ pid_t sys_waitpid(pid_t pid, int *status, int options) scheduler_dequeue_task(child); // Remove from the scheduler. kmem_cache_free(child); // Free the `task_struct`. - pr_debug("Process %d cleaned up child process %d.\n", runqueue.curr->pid, child_pid); + pr_debug("Process %d (%s) CLEANED UP process %d.\n", runqueue.curr->pid, runqueue.curr->name, child_pid); // Return the PID of the cleaned-up child. return child_pid; @@ -628,19 +640,20 @@ pid_t sys_waitpid(pid_t pid, int *status, int options) } // Otherwise, block until a child exits (and sends SIGCHLD to wake us up). - pr_debug("Process %d sleeping in waitpid (no zombie child yet)\n", runqueue.curr->pid); + // sleep_on() will dequeue this task and mark it uninterruptible. + // Context switch will happen when this syscall returns and syscall_handler calls scheduler_run(f). + // When woken up (by wake_up_process_on_queue in do_exit), this task will be + // re-enqueued and eventually scheduled again, returning to userspace with -EINTR. + // Userspace must retry the syscall, which will then find and reap the zombie. + pr_debug("Process %d (%s) sleeping in waitpid (no zombie child yet)\n", runqueue.curr->pid, runqueue.curr->name); wait_queue_entry_t *wait_entry = sleep_on(&waitpid_queue); if (!wait_entry) { pr_err("Failed to sleep in waitpid\n"); return -ENOMEM; } - // After being woken up (by SIGCHLD), retry the search for zombie children. - // The signal handler will have already moved us back to the runqueue. - pr_debug("Process %d woken up from waitpid sleep\n", runqueue.curr->pid); - - // Return -EINTR to indicate the syscall was interrupted by a signal. - // The userspace waitpid() wrapper will retry the syscall. + // Return -EINTR to indicate the syscall was interrupted. + // The userspace waitpid() wrapper should retry the syscall on -EINTR. return -EINTR; } @@ -654,6 +667,8 @@ void do_exit(int exit_code) kernel_panic("Init process cannot call sys_exit!"); } + pr_debug("Process %d is exiting with code %d.\n", runqueue.curr->pid, exit_code); + // Set the termination code of the process. runqueue.curr->exit_code = exit_code; // Set the state of the process to zombie. @@ -665,11 +680,11 @@ void do_exit(int exit_code) pr_err( "[%d] %5d failed sending signal %d : %s\n", ret, runqueue.curr->parent->pid, SIGCHLD, strerror(errno)); } + // Wake up the parent if it's sleeping in waitpid(). + // Only wake the parent, not all processes waiting on this queue. + wake_up_process_on_queue(&waitpid_queue, runqueue.curr->parent); } - // Wake up any processes sleeping in waitpid(). - wake_up_all(&waitpid_queue); - // If it has children, then init process has to take care of them. if (!list_head_empty(&runqueue.curr->children)) { pr_debug( diff --git a/kernel/src/process/wait.c b/kernel/src/process/wait.c index 6b196643..d14dc607 100644 --- a/kernel/src/process/wait.c +++ b/kernel/src/process/wait.c @@ -4,10 +4,10 @@ /// See LICENSE.md for details. // Setup the logging for this file (do this before any other include). -#include "sys/kernel_levels.h" // Include kernel log levels. -#define __DEBUG_HEADER__ "[WAIT ]" ///< Change header. +#include "sys/kernel_levels.h" // Include kernel log levels. +#define __DEBUG_HEADER__ "[WAIT ]" ///< Change header. #define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. -#include "io/debug.h" // Include debugging functions. +#include "io/debug.h" // Include debugging functions. #include "process/wait.h" @@ -18,6 +18,9 @@ #include "string.h" #include +/// The list of processes. +extern runqueue_t runqueue; + /// @brief Adds the entry to the wait queue. /// @param head the wait queue. /// @param entry the entry. @@ -96,7 +99,7 @@ wait_queue_entry_t *wait_queue_entry_alloc(void) { // Allocate the memory. wait_queue_entry_t *entry = (wait_queue_entry_t *)kmalloc(sizeof(wait_queue_entry_t)); - pr_debug("ALLOCATE wait_queue_entry_t %p\n", entry); + // pr_debug("ALLOCATE wait_queue_entry_t %p\n", entry); // Check the allocated memory. assert(entry && "Failed to allocate memory for a wait_queue_entry_t."); // Clean the memory. @@ -114,7 +117,7 @@ wait_queue_entry_t *wait_queue_entry_alloc(void) void wait_queue_entry_dealloc(wait_queue_entry_t *entry) { assert(entry && "Received a NULL pointer."); - pr_debug("FREE wait_queue_entry_t %p\n", entry); + // pr_debug("FREE wait_queue_entry_t %p\n", entry); // Deallocate the memory. kfree(entry); } @@ -131,10 +134,12 @@ void wake_up_all(wait_queue_head_t *head) { wait_queue_entry_t *entry = list_entry(it, wait_queue_entry_t, task_list); if (entry->func(entry, TASK_RUNNING, 0) || entry->task->state == TASK_RUNNING) { + // Debug on the output. + pr_debug("Process %d (%s) WOKEN UP from %s\n", entry->task->pid, entry->task->name, head->name); + // Remove the entry from the wait queue and re-enqueue the task if it's not already running. remove_wait_queue(head, entry); // Clear the wait queue tracking field when removing from queue entry->task->waiting_on = NULL; - pr_debug("wake_up_all(%p): waking process %d\n", head, entry->task->pid); /* only enqueue if not already on runqueue */ if (list_head_empty(&entry->task->run_list)) { scheduler_enqueue_task(entry->task); @@ -144,6 +149,44 @@ void wake_up_all(wait_queue_head_t *head) } } +int wake_up_process_on_queue(wait_queue_head_t *head, struct task_struct *task) +{ + if (!head) { + pr_err("wake_up_process_on_queue: head is NULL\n"); + return 0; + } + if (!task) { + pr_err("wake_up_process_on_queue: task is NULL\n"); + return 0; + } + + // Search for the specific task in the wait queue + list_for_each_safe_decl(it, store, &head->task_list) + { + wait_queue_entry_t *entry = list_entry(it, wait_queue_entry_t, task_list); + // Check if this entry corresponds to our target task + if (entry->task == task) { + // Try to wake up the task using its wake function + if (entry->func(entry, TASK_RUNNING, 0) || entry->task->state == TASK_RUNNING) { + pr_debug("Process %d (%s) WOKEN UP from %s\n", entry->task->pid, entry->task->name, head->name); + // Remove the entry from the wait queue + remove_wait_queue(head, entry); + // Clear the wait queue tracking field + entry->task->waiting_on = NULL; + // Re-enqueue the task if it's not already on the runqueue + if (list_head_empty(&entry->task->run_list)) { + scheduler_enqueue_task(entry->task); + } + wait_queue_entry_dealloc(entry); + return 1; // Successfully woken up + } + } + } + + // Task was not found in this wait queue + return 0; +} + void wait_queue_entry_init(wait_queue_entry_t *entry, struct task_struct *task) { // Validate the input. @@ -193,6 +236,8 @@ void remove_wait_queue(wait_queue_head_t *head, wait_queue_entry_t *entry) spinlock_lock(&head->lock); __remove_wait_queue(head, entry); spinlock_unlock(&head->lock); + + pr_debug("Removed process %d (%s) from wait queue %s (entry %p)\n", entry->task->pid, entry->task->name, head->name, entry); } wait_queue_entry_t *sleep_on(wait_queue_head_t *head) @@ -216,6 +261,13 @@ wait_queue_entry_t *sleep_on(wait_queue_head_t *head) // inserting the entry; IRQs are restored before returning so normal operation resumes. uint8_t irqs = irq_disable(); + wait_queue_entry_t *entry = wait_queue_entry_alloc(); + if (!entry) { + pr_err("Failed to allocate memory for wait queue entry.\n"); + irq_enable(irqs); + return NULL; + } + // Set the task state to uninterruptible to indicate it is sleeping. sleeping_task->state = TASK_UNINTERRUPTIBLE; @@ -223,25 +275,7 @@ wait_queue_entry_t *sleep_on(wait_queue_head_t *head) sleeping_task->waiting_on = head; // Remove task from runqueue so scheduler will ignore it while blocked. - // Guard against double-dequeue: run_list is self-linked when not queued. - if (!list_head_empty(&sleeping_task->run_list)) { - scheduler_dequeue_task(sleeping_task); - } - - // Allocate memory for a new wait queue entry. - wait_queue_entry_t *entry = wait_queue_entry_alloc(); - if (!entry) { - pr_err("Failed to allocate memory for wait queue entry.\n"); - // Roll back sleeping state to avoid leaving the current process - // blocked and dequeued after allocation failure. - sleeping_task->state = TASK_RUNNING; - sleeping_task->waiting_on = NULL; - if (list_head_empty(&sleeping_task->run_list)) { - scheduler_enqueue_task(sleeping_task); - } - irq_enable(irqs); - return NULL; - } + scheduler_dequeue_task(sleeping_task); // Initialize the wait queue entry with the current task. wait_queue_entry_init(entry, sleeping_task); @@ -252,7 +286,7 @@ wait_queue_entry_t *sleep_on(wait_queue_head_t *head) /* restore interrupts before returning */ irq_enable(irqs); - pr_debug("Added process %d to wait queue %p (entry %p)\n", sleeping_task->pid, head, entry); + pr_debug("Process %d (%s) SLEEPS ON %s (entry %p)\n", sleeping_task->pid, sleeping_task->name, head->name, entry); return entry; } diff --git a/kernel/src/system/signal.c b/kernel/src/system/signal.c index 48fe6291..5bf80fb4 100644 --- a/kernel/src/system/signal.c +++ b/kernel/src/system/signal.c @@ -27,7 +27,11 @@ static kmem_cache_t *sigqueue_cachep; /// Contains all stopped process waiting for a continue signal -static wait_queue_head_t stopped_queue; +static wait_queue_head_t stopped_queue = { + .name = "stopped_queue", + .lock = SPINLOCK_INIT, + .task_list = LIST_HEAD_INIT(stopped_queue.task_list), +}; /// @brief The list of signal names. static const char *sys_siglist[] = { @@ -764,7 +768,7 @@ int sys_kill(pid_t pid, int sig) info.si_addr = NULL; info.si_status = 0; info.si_band = 0; - int ret = __send_sig_info(sig, &info, process); + int ret = __send_sig_info(sig, &info, process); // If the target process is sleeping on a wait queue, remove it from the // queue and re-enqueue it to the runqueue so the scheduler will run it @@ -774,11 +778,11 @@ int sys_kill(pid_t pid, int sig) // The task is sleeping on a wait queue. We need to find and remove // the entry from the wait queue, then move it to the runqueue. wait_queue_head_t *wait_queue = process->waiting_on; - wait_queue_entry_t *entry = NULL; + wait_queue_entry_t *entry = NULL; // Acquire the wait queue lock and search for the entry. spinlock_lock(&wait_queue->lock); - list_for_each_decl(it, &wait_queue->task_list) { + list_for_each_decl (it, &wait_queue->task_list) { wait_queue_entry_t *candidate = list_entry(it, wait_queue_entry_t, task_list); if (candidate->task == process) { entry = candidate; @@ -798,12 +802,10 @@ int sys_kill(pid_t pid, int sig) scheduler_enqueue_task(process); } wait_queue_entry_dealloc(entry); - pr_debug("sys_kill: removed task %d from wait queue %p and re-enqueued\n", - process->pid, wait_queue); + pr_debug("sys_kill: removed task %d from wait queue %p and re-enqueued\n", process->pid, wait_queue); } else { spinlock_unlock(&wait_queue->lock); - pr_warning("sys_kill: task %d claims to sleep on wait queue %p but not found\n", - process->pid, wait_queue); + pr_warning("sys_kill: task %d claims to sleep on wait queue %p but not found\n", process->pid, wait_queue); } } else if (process->state == TASK_UNINTERRUPTIBLE || process->state == TASK_INTERRUPTIBLE) { // Task is sleeping but not on a wait queue (shouldn't happen). diff --git a/lib/inc/list_head.h b/lib/inc/list_head.h index af8012b0..3a5d0d29 100644 --- a/lib/inc/list_head.h +++ b/lib/inc/list_head.h @@ -14,6 +14,11 @@ typedef struct list_head { struct list_head *next; ///< The subsequent element. } list_head_t; +#define LIST_HEAD_INIT(name) \ + { \ + &(name), &(name) \ + } + /// @brief Get the struct for this entry. /// @param ptr The &list_head pointer. /// @param type The type of the struct this is embedded in. @@ -29,7 +34,7 @@ typedef struct list_head { /// @param pos the name of the iterator used to visit the list. /// @param store another list iterator to use as temporary storage. /// @param head the head for your list. -#define list_for_each_safe(pos, store, head) \ +#define list_for_each_safe(pos, store, head) \ for ((pos) = (head)->next, (store) = (pos)->next; (pos) != (head); (pos) = (store), (store) = (pos)->next) /// @brief Iterates over a list, but declares the iterator. @@ -41,8 +46,8 @@ typedef struct list_head { /// @param pos the name of the iterator used to visit the list. /// @param store another list iterator to use as temporary storage. /// @param head the head for your list. -#define list_for_each_safe_decl(pos, store, head) \ - for (list_head_t * (pos) = (head)->next, *(store) = (pos)->next; (pos) != (head); \ +#define list_for_each_safe_decl(pos, store, head) \ + for (list_head_t * (pos) = (head)->next, *(store) = (pos)->next; (pos) != (head); \ (pos) = (store), (store) = (pos)->next) /// @brief Iterates over a list backwards. @@ -53,7 +58,7 @@ typedef struct list_head { /// @brief Iterates over a list backwards, but declares the iterator. /// @param pos the name of the iterator used to visit the list. /// @param head the head for your list. -#define list_for_each_prev_decl(pos, head) \ +#define list_for_each_prev_decl(pos, head) \ for (list_head_t * (pos) = (head)->prev; (pos) != (head); (pos) = (pos)->prev) /// @brief Ensures that the given list is valid. diff --git a/userspace/bin/init.c b/userspace/bin/init.c index 1a25c6da..ca98be85 100644 --- a/userspace/bin/init.c +++ b/userspace/bin/init.c @@ -11,10 +11,6 @@ int main(int argc, char *argv[], char *envp[]) { char *_argv[] = {"login", NULL}; - int status; - -#pragma clang diagnostic push -#pragma ide diagnostic ignored "EndlessLoop" while (1) { pid_t login = fork(); if (login == 0) { @@ -22,9 +18,8 @@ int main(int argc, char *argv[], char *envp[]) printf("This is bad, I should not be here! EXEC NOT WORKING\n"); } - while (wait(&status) != login) { + while (waitpid(-1, NULL, 0) != login) { } } -#pragma clang diagnostic pop return 0; } From b4384e69b5eb12c49d63fbbf67454b77c2fba85f Mon Sep 17 00:00:00 2001 From: "Enrico Fraccaroli (Galfurian)" Date: Fri, 6 Mar 2026 11:42:11 +0100 Subject: [PATCH 13/22] fix(scheduler): prevent double-enqueue/dequeue and improve debugging - Add safety checks in scheduler_enqueue_task and scheduler_dequeue_task to detect and prevent double-enqueue/dequeue bugs - Implement __dump_runqueue function for debugging scheduler state - Remove verbose debug logging from enqueue/dequeue operations - Change log levels from DEBUG to NOTICE in scheduler, process, timer, and wait modules - Enhance shell status handling using environment variables instead of globals - Add comprehensive syslog debugging in shell for command execution and signal handling - Update VSCode editor settings for consistent formatting --- .vscode/settings.json | 8 ++- kernel/src/hardware/timer.c | 2 +- kernel/src/process/process.c | 2 +- kernel/src/process/scheduler.c | 56 +++++++++++------- kernel/src/process/scheduler_algorithm.c | 2 +- kernel/src/process/wait.c | 11 ++-- userspace/bin/shell.c | 73 +++++++++++++++++------- 7 files changed, 102 insertions(+), 52 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index ee38262e..08ac39a1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,5 +4,11 @@ "*.h": "c", "*.tcc": "c", "functional": "c" - } + }, + "editor.wordWrapColumn": 80, + "rewrap.wrappingColumn": 80, + "editor.rulers": [ + 80, + 120 + ], } \ No newline at end of file diff --git a/kernel/src/hardware/timer.c b/kernel/src/hardware/timer.c index 971feb8d..6d0a424f 100644 --- a/kernel/src/hardware/timer.c +++ b/kernel/src/hardware/timer.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TIMER ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/process/process.c b/kernel/src/process/process.c index 5c041557..81e05836 100644 --- a/kernel/src/process/process.c +++ b/kernel/src/process/process.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[PROC ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/process/scheduler.c b/kernel/src/process/scheduler.c index a548949c..f93cbba3 100644 --- a/kernel/src/process/scheduler.c +++ b/kernel/src/process/scheduler.c @@ -4,10 +4,10 @@ /// See LICENSE.md for details. // Setup the logging for this file (do this before any other include). -#include "sys/kernel_levels.h" // Include kernel log levels. -#define __DEBUG_HEADER__ "[SCHED ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. -#include "io/debug.h" // Include debugging functions. +#include "sys/kernel_levels.h" // Include kernel log levels. +#define __DEBUG_HEADER__ "[SCHED ]" ///< Change header. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#include "io/debug.h" // Include debugging functions. #include "assert.h" #include "descriptor_tables/tss.h" @@ -41,6 +41,15 @@ static wait_queue_head_t waitpid_queue = { .task_list = LIST_HEAD_INIT(waitpid_queue.task_list), }; +void __dump_runqueue(int log_level) +{ + pr_log(log_level, "Dumping runqueue (num_active: %zu, num_periodic: %zu):\n", runqueue.num_active, runqueue.num_periodic); + list_for_each_decl (it, &runqueue.queue) { + task_struct *entry = list_entry(it, task_struct, run_list); + pr_log(log_level, " PID %d (%s) - state: %ld, vruntime: %lu\n", entry->pid, entry->name, entry->state, entry->se.vruntime); + } +} + void scheduler_initialize(void) { // Initialize the runqueue list of tasks. @@ -98,6 +107,13 @@ task_struct *scheduler_get_running_process(pid_t pid) void scheduler_enqueue_task(task_struct *process) { assert(process && "Received a NULL process."); + + // If the process is already in a list, raise an error. + if (!list_head_empty(&process->run_list)) { + pr_err("scheduler_enqueue_task: Process %d is already in a list.\n", process->pid); + __dump_runqueue(LOGLEVEL_ERR); + return; + } // If current_process is NULL, then process is the current process. if (runqueue.curr == NULL) { runqueue.curr = process; @@ -107,14 +123,6 @@ void scheduler_enqueue_task(task_struct *process) // Increment the number of active processes. ++runqueue.num_active; - // Print the entire running queue for debugging purposes. - pr_debug("scheduler_enqueue_task(%d): runqueue: [", process->pid); - list_for_each_decl (running_it, &runqueue.queue) { - task_struct *running_task = list_entry(running_it, task_struct, run_list); - pr_debug("%d ", running_task->pid); - } - pr_debug("]\n"); - #ifdef ENABLE_SCHEDULER_FEEDBACK scheduler_feedback_task_add(process); #endif @@ -123,6 +131,13 @@ void scheduler_enqueue_task(task_struct *process) void scheduler_dequeue_task(task_struct *process) { assert(process && "Received a NULL process."); + + // If the process is not in a list, raise an error. + if (list_head_empty(&process->run_list)) { + pr_err("scheduler_dequeue_task: Process %d is not in a list.\n", process->pid); + __dump_runqueue(LOGLEVEL_ERR); + return; + } // Delete the process from the list of running processes. list_head_remove(&process->run_list); // Decrement the number of active processes. @@ -130,15 +145,6 @@ void scheduler_dequeue_task(task_struct *process) if (process->se.is_periodic) { runqueue.num_periodic--; } - - // Print the entire running queue for debugging purposes. - pr_debug("scheduler_dequeue_task(%d): runqueue: [", process->pid); - list_for_each_decl (running_it, &runqueue.queue) { - task_struct *running_task = list_entry(running_it, task_struct, run_list); - pr_debug("%d ", running_task->pid); - } - pr_debug("]\n"); - #ifdef ENABLE_SCHEDULER_FEEDBACK scheduler_feedback_task_remove(process->pid); #endif @@ -183,15 +189,23 @@ void scheduler_run(pt_regs_t *f) // scheduler_pick_next_task call above will have already re-enabled the cpu if necessary. if (!next) { if (runqueue.num_active == 0) { + + pr_debug("SCHEDULER_RUN: No active tasks in the runqueue.\n"); + while (runqueue.num_active == 0) { sti(); __asm__ __volatile__("hlt"); cli(); } + next = scheduler_pick_next_task(&runqueue); + pr_debug("SCHEDULER_RUN: No active tasks, idling until a task is enqueued. Next task: %d\n", next ? next->pid : -1); } if (!next) { + + pr_debug("SCHEDULER_RUN: No runnable tasks found in the runqueue.\n"); + // Resume current only if it is still runnable and queued. if ((runqueue.curr->state == TASK_RUNNING) && !list_head_empty(&runqueue.curr->run_list)) { next = runqueue.curr; diff --git a/kernel/src/process/scheduler_algorithm.c b/kernel/src/process/scheduler_algorithm.c index 400a518d..fb475d13 100644 --- a/kernel/src/process/scheduler_algorithm.c +++ b/kernel/src/process/scheduler_algorithm.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[SCHALG]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "assert.h" diff --git a/kernel/src/process/wait.c b/kernel/src/process/wait.c index d14dc607..482472d0 100644 --- a/kernel/src/process/wait.c +++ b/kernel/src/process/wait.c @@ -4,10 +4,10 @@ /// See LICENSE.md for details. // Setup the logging for this file (do this before any other include). -#include "sys/kernel_levels.h" // Include kernel log levels. -#define __DEBUG_HEADER__ "[WAIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. -#include "io/debug.h" // Include debugging functions. +#include "sys/kernel_levels.h" // Include kernel log levels. +#define __DEBUG_HEADER__ "[WAIT ]" ///< Change header. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#include "io/debug.h" // Include debugging functions. #include "process/wait.h" @@ -18,9 +18,6 @@ #include "string.h" #include -/// The list of processes. -extern runqueue_t runqueue; - /// @brief Adds the entry to the wait queue. /// @param head the wait queue. /// @param entry the entry. diff --git a/userspace/bin/shell.c b/userspace/bin/shell.c index 59b69ebc..0d739244 100644 --- a/userspace/bin/shell.c +++ b/userspace/bin/shell.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -43,10 +44,6 @@ DECLARE_FIXED_SIZE_2D_RING_BUFFER(char, history, HISTORY_MAX, CMD_LEN, 0) static rb_history_t history; // History reading index. static unsigned history_index; -// Store the last command status -static int status = 0; -// Store the last command status as string -static char status_buf[4] = {0}; static sigset_t oldmask; @@ -242,9 +239,12 @@ static char *__getenv(const char *var) } // Handle special variables like `$?`, which represents the status of the last command. if (var[0] == '?') { - // Assuming 'status' is a global or accessible variable containing the last command status. - sprintf(status_buf, "%d", status); // Convert the status to a string. - return status_buf; // Return the status as a string. + // Get the 'status' environment variable, which should have been set by the last command execution. + char *status_str = getenv("status"); + if (status_str == NULL) { + return "0"; // Default to "0" if the status variable is not set. + } + return status_str; // Return the status as a string. } // TODO: Implement access to `argv` for positional parameters (e.g., $1, $2). #if 0 @@ -822,7 +822,7 @@ static inline int __read_command(rb_history_entry_t *entry) // Handle newline character to finish input if (c == '\n') { - putchar('\n'); // Display a newline + putchar('\n'); // Display a newline return buffer_full ? -2 : length; // Return -2 if buffer was full } @@ -1205,7 +1205,10 @@ static void __setup_redirects(int *argcp, char ***argvp) /// @return Returns the exit status of the command. static int __execute_command(rb_history_entry_t *entry) { - int _status = 0; + syslog(LOG_DEBUG, "==========\n"); + syslog(LOG_DEBUG, "shell: executing command: %s\n", entry->buffer); + + int status = 0; // Variable to store the exit status of the command. // Retrieve the arguments from the command buffer. int _argc = 1; // Initialize the argument count. @@ -1245,8 +1248,10 @@ static int __execute_command(rb_history_entry_t *entry) __block_sigchld(); // Fork the current process to create a child process. pid_t cpid = fork(); + syslog(LOG_DEBUG, "shell: fork() returned cpid=%d\n", cpid); if (cpid == 0) { // Child process: Execute the command. + syslog(LOG_DEBUG, "shell: child process, executing: %s\n", _argv[0]); pid_t pid = getpid(); setpgid(cpid, pid); // Make the new process a group leader. __unblock_sigchld(); // Unblock SIGCHLD signals in the child process. @@ -1260,20 +1265,23 @@ static int __execute_command(rb_history_entry_t *entry) } if (blocking) { // Parent process: Wait for the child process to finish, retrying on EINTR. + syslog(LOG_DEBUG, "shell: parent blocking in waitpid for cpid=%d\n", cpid); int ret; do { - ret = waitpid(cpid, &_status, 0); + ret = waitpid(cpid, &status, 0); + syslog(LOG_DEBUG, "shell: waitpid returned ret=%d errno=%d\n", ret, errno); } while (ret == -1 && errno == EINTR); + syslog(LOG_DEBUG, "shell: waitpid finished, status=%d\n", status); // Handle different exit statuses of the child process. - if (WIFSIGNALED(_status)) { + if (WIFSIGNALED(status)) { printf( - FG_RED "\nExit status %d, killed by signal %d\n" FG_RESET, WEXITSTATUS(_status), WTERMSIG(_status)); - } else if (WIFSTOPPED(_status)) { + FG_RED "\nExit status %d, killed by signal %d\n" FG_RESET, WEXITSTATUS(status), WTERMSIG(status)); + } else if (WIFSTOPPED(status)) { printf( - FG_YELLOW "\nExit status %d, stopped by signal %d\n" FG_RESET, WEXITSTATUS(_status), - WSTOPSIG(_status)); - } else if (WEXITSTATUS(_status) != 0) { - printf(FG_RED "\nExit status %d\n" FG_RESET, WEXITSTATUS(_status)); + FG_YELLOW "\nExit status %d, stopped by signal %d\n" FG_RESET, WEXITSTATUS(status), + WSTOPSIG(status)); + } else if (WEXITSTATUS(status) != 0) { + printf(FG_RED "\nExit status %d\n" FG_RESET, WEXITSTATUS(status)); } } __unblock_sigchld(); // Unblock SIGCHLD signals after command execution. @@ -1281,12 +1289,13 @@ static int __execute_command(rb_history_entry_t *entry) // Free the memory allocated for the argument list. __free_argv(_argc, _argv); // Update the global status variable with the exit status of the command. - status = WEXITSTATUS(_status); + status = WEXITSTATUS(status); return status; // Return the exit status of the command. } static int __execute_file(char *path) { + int status = 0; rb_history_entry_t entry; rb_history_init_entry(&entry); int fd; @@ -1359,20 +1368,44 @@ static void __interactive_mode(void) } // Execute the command. - __execute_command(&entry); + int status = __execute_command(&entry); + + // Set in the environment the variable "status" with the exit status of the last command. + char status_str[4]; + snprintf(status_str, sizeof(status_str), "%d", status); + setenv("status", status_str, 1); } #pragma clang diagnostic pop } void wait_for_child(int signum) { + int status = 0; + pid_t child_pid; // Reap all terminated children without blocking. - while (waitpid(-1, NULL, WNOHANG) > 0) { + while ((child_pid = waitpid(-1, &status, WNOHANG)) > 0) { + syslog(LOG_DEBUG, "shell: SIGCHLD handler reaped child pid=%d, status=%d\n", child_pid, status); } + syslog(LOG_DEBUG, "shell: SIGCHLD handler finished reaping children, last waitpid returned pid=%d, errno=%d, status=%d\n", child_pid, errno, status); + + // Set in the environment the variable "status" with the exit status of the last command. + char status_str[4]; + snprintf(status_str, sizeof(status_str), "%d", status); + setenv("status", status_str, 1); } int main(int argc, char *argv[]) { + // Initialize syslog for debugging + openlog("shell", LOG_CONS | LOG_PID, LOG_USER); + + // Set log mask to include info messages. + setlogmask(LOG_UPTO(LOG_INFO)); + + syslog(LOG_INFO, "shell: starting, pid=%d\n", getpid()); + + int status = 0; // Variable to store the exit status of commands. + setsid(); // Initialize the history. rb_history_init(&history, rb_history_entry_copy); From 4e34111ec2fc764b6588c28e1d024888ec64c8a2 Mon Sep 17 00:00:00 2001 From: "Enrico Fraccaroli (Galfurian)" Date: Fri, 6 Mar 2026 12:05:30 +0100 Subject: [PATCH 14/22] fix(scheduler): restore wake-up scheduling for sleeping tasks - re-enqueue task on sleep timer expiry after removing wait queue entry\n- clear waiting_on when waking from sleep timeout\n- make RR selection robust when current task is dequeued\n- guard runtime stats update to runnable queued current task --- kernel/src/hardware/timer.c | 5 +++ kernel/src/process/scheduler_algorithm.c | 57 ++++++++++++------------ 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/kernel/src/hardware/timer.c b/kernel/src/hardware/timer.c index 6d0a424f..6bba2b97 100644 --- a/kernel/src/hardware/timer.c +++ b/kernel/src/hardware/timer.c @@ -575,6 +575,11 @@ static inline void sleep_timeout(unsigned long data) pr_debug("Process (pid: %d) restored from sleep\n", wait_queue_entry->task->pid); // Removes entry from list and memory. remove_wait_queue(&sleep_queue, wait_queue_entry); + // Clear wait queue tracking and re-enqueue if the task is currently not runnable. + wait_queue_entry->task->waiting_on = NULL; + if (list_head_empty(&wait_queue_entry->task->run_list)) { + scheduler_enqueue_task(wait_queue_entry->task); + } // Free the memory of the wait queue item. wait_queue_entry_dealloc(wait_queue_entry); // Free the memory of the sleep_data. diff --git a/kernel/src/process/scheduler_algorithm.c b/kernel/src/process/scheduler_algorithm.c index fb475d13..0c6e3406 100644 --- a/kernel/src/process/scheduler_algorithm.c +++ b/kernel/src/process/scheduler_algorithm.c @@ -39,36 +39,32 @@ static inline bool_t __is_periodic_task(task_struct *task) /// @return the next task on success, NULL on failure. static inline task_struct *__scheduler_rr(runqueue_t *runqueue, bool_t skip_periodic) { - // If there is just one task in the list, we normally would return it. - // however, if the current task is not in a runnable state we must - // indicate that no suitable candidate exists. - if (list_head_size(&runqueue->curr->run_list) <= 1) { - if (runqueue->curr->state == TASK_RUNNING) { - return runqueue->curr; - } else { - return NULL; + // If the current task is still enqueued, start searching from its next node + // to preserve round-robin fairness. Otherwise, start from the runqueue head. + list_head_t *start = list_head_empty(&runqueue->curr->run_list) + ? runqueue->queue.next + : runqueue->curr->run_list.next; + list_head_t *it = start; + + // Iterate at most one full runqueue cycle. + while (it != &runqueue->queue) { + task_struct *entry = list_entry(it, task_struct, run_list); + if ((entry->state == TASK_RUNNING) && + !(__is_periodic_task(entry) && skip_periodic)) { + return entry; } + it = it->next; } - // This will hold a given entry, while iterating the list of tasks. - task_struct *entry = NULL; - // Search for the next task (we do not start from the head, so INSIDE, skip the head). - list_for_each_decl (it, &runqueue->curr->run_list) { - // Check if we reached the head of list_head, and skip it. - if (it == &runqueue->queue) { - continue; - } - // Get the current entry. - entry = list_entry(it, task_struct, run_list); - // We consider only runnable processes. - if (entry->state != TASK_RUNNING) { - continue; - } - // If entry is a periodic task, and we were asked to skip periodic tasks, skip it. - if (__is_periodic_task(entry) && skip_periodic) { - continue; + + // Wrap once if we started from current->next and did not reach start yet. + it = runqueue->queue.next; + while ((it != &runqueue->queue) && (it != start)) { + task_struct *entry = list_entry(it, task_struct, run_list); + if ((entry->state == TASK_RUNNING) && + !(__is_periodic_task(entry) && skip_periodic)) { + return entry; } - // We have our next entry. - return entry; + it = it->next; } return NULL; @@ -366,8 +362,11 @@ static inline task_struct *__scheduler_rm(runqueue_t *runqueue) task_struct *scheduler_pick_next_task(runqueue_t *runqueue) { - // Update task statistics. - __update_task_statistics(runqueue->curr); + // Update statistics only for a runnable task still present in the runqueue. + if (runqueue->curr && (runqueue->curr->state == TASK_RUNNING) && + !list_head_empty(&runqueue->curr->run_list)) { + __update_task_statistics(runqueue->curr); + } // Pointer to the next task to schedule. task_struct *next = NULL; From 383fb0d0076b204e4f2718e2d7c0ff405dfa9507 Mon Sep 17 00:00:00 2001 From: "Enrico Fraccaroli (Galfurian)" Date: Fri, 6 Mar 2026 12:06:44 +0100 Subject: [PATCH 15/22] fix(pipe): re-enqueue tasks woken from pipe wait queues - clear waiting_on when removing wait queue entries\n- enqueue task back to runqueue after wake-up if not already queued\n- prevent TASK_RUNNING tasks from being lost outside scheduler runqueue --- kernel/src/fs/pipe.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/kernel/src/fs/pipe.c b/kernel/src/fs/pipe.c index 93b37749..997ae902 100644 --- a/kernel/src/fs/pipe.c +++ b/kernel/src/fs/pipe.c @@ -572,6 +572,12 @@ static void pipe_wake_up_tasks(wait_queue_head_t *wait_queue, const char *debug_ // Task is ready, remove from the wait queue. remove_wait_queue(wait_queue, wait_queue_entry); + // Clear wait queue tracking and re-enqueue task if needed. + wait_queue_entry->task->waiting_on = NULL; + if (list_head_empty(&wait_queue_entry->task->run_list)) { + scheduler_enqueue_task(wait_queue_entry->task); + } + // Log and free the memory associated with the wait entry. pr_debug("%s: Process %d woken up.\n", debug_msg, wait_queue_entry->task->pid); wait_queue_entry_dealloc(wait_queue_entry); From 251b557b02a79606364b7352074b8b7d7578e2b1 Mon Sep 17 00:00:00 2001 From: "Enrico Fraccaroli (Galfurian)" Date: Fri, 6 Mar 2026 14:41:38 +0100 Subject: [PATCH 16/22] refactor(scheduler): centralize task wake-up management Separate concerns: subsystems manage wait queues, scheduler manages state. wake_up_process() now only handles task state transitions and runqueue placement. Each subsystem removes its entries before calling wake_up_process(). --- kernel/inc/process/process.h | 2 + kernel/inc/process/scheduler.h | 11 +++++ kernel/src/fs/pipe.c | 19 ++++---- kernel/src/hardware/timer.c | 82 ++++++++++++++++++++++++++++------ kernel/src/process/scheduler.c | 67 ++++++++++++++++++++++++--- kernel/src/process/wait.c | 69 +++++++++++++++------------- kernel/src/system/signal.c | 77 +++++++------------------------ userspace/tests/t_kill.c | 20 +++++---- 8 files changed, 218 insertions(+), 129 deletions(-) diff --git a/kernel/inc/process/process.h b/kernel/inc/process/process.h index b748e2ef..e5bfd149 100644 --- a/kernel/inc/process/process.h +++ b/kernel/inc/process/process.h @@ -139,6 +139,8 @@ typedef struct task_struct { /// Timer for alarm syscall. struct timer_list *real_timer; + /// Timer for nanosleep syscall (cancelled on signal delivery). + struct timer_list *sleep_timer; /// Next value for the real timer (ITIMER_REAL). unsigned long it_real_incr; diff --git a/kernel/inc/process/scheduler.h b/kernel/inc/process/scheduler.h index c5e4398d..72102704 100644 --- a/kernel/inc/process/scheduler.h +++ b/kernel/inc/process/scheduler.h @@ -69,6 +69,17 @@ void scheduler_enqueue_task(task_struct *process); /// @param process Process that has to be activated. void scheduler_dequeue_task(task_struct *process); +/// @brief Wake up a sleeping/stopped task and make it runnable. +/// @details Handles all state transitions, queue management, and cleanup atomically: +/// - Transitions task to TASK_RUNNING state +/// - Removes task from any wait queue it's sleeping on +/// - Cancels any active sleep timer +/// - Enqueues task to scheduler runqueue +/// - Clears wait queue tracking fields +/// @param task The task to wake up +/// @return 0 on success, negative on error +int wake_up_process(task_struct *task); + /// @brief The RR implementation of the scheduler. /// @param f The context of the process. void scheduler_run(pt_regs_t *f); diff --git a/kernel/src/fs/pipe.c b/kernel/src/fs/pipe.c index 997ae902..9a32cd34 100644 --- a/kernel/src/fs/pipe.c +++ b/kernel/src/fs/pipe.c @@ -569,17 +569,16 @@ static void pipe_wake_up_tasks(wait_queue_head_t *wait_queue, const char *debug_ // Run the wakeup test function for the waiting task. if (wait_queue_entry->func(wait_queue_entry, TASK_RUNNING, 0)) { - // Task is ready, remove from the wait queue. + // Task is ready to be woken up. + pr_debug("%s: waking up process %d\n", debug_msg, wait_queue_entry->task->pid); + + // Remove from the pipe's wait queue (this subsystem owns the entry). remove_wait_queue(wait_queue, wait_queue_entry); - - // Clear wait queue tracking and re-enqueue task if needed. - wait_queue_entry->task->waiting_on = NULL; - if (list_head_empty(&wait_queue_entry->task->run_list)) { - scheduler_enqueue_task(wait_queue_entry->task); - } - - // Log and free the memory associated with the wait entry. - pr_debug("%s: Process %d woken up.\n", debug_msg, wait_queue_entry->task->pid); + + // Call centralized scheduler function to handle state and enqueue. + wake_up_process(wait_queue_entry->task); + + // Clean up the wait queue entry. wait_queue_entry_dealloc(wait_queue_entry); } } diff --git a/kernel/src/hardware/timer.c b/kernel/src/hardware/timer.c index 6bba2b97..b90533eb 100644 --- a/kernel/src/hardware/timer.c +++ b/kernel/src/hardware/timer.c @@ -562,6 +562,50 @@ static inline void debug_timeout(unsigned long data) data, timer_ticks, timer_get_seconds()); } +/// @brief Cancels a sleep timer for a task being woken up by signals. +/// This handles the race where a signal interrupts a sleeping task: +/// the signal handler calls this to stop the sleep timer from firing. +/// +/// With boundary-based context switching, we don't need to trigger +/// immediate scheduling - the signal handler will return to the next +/// interrupt/exception boundary where scheduler_run() picks the next task. +/// +/// @param task The task whose sleep timer should be canceled. +/// @return 0 on success, -1 if no sleep timer exists. +int cancel_sleep_timer(struct task_struct *task) +{ + // Check if the task has a sleep timer. + if (!task || !task->sleep_timer) { + return -1; + } + + // Remove the timer from the timer system. + remove_timer(task->sleep_timer); + + // Extract the sleep data to prevent the timer callback from using stale pointers. + sleep_data_t *sleep_data = (sleep_data_t *)task->sleep_timer->data; + + // Mark the wait_queue_entry as NULL so if the timer fires during removal, + // sleep_timeout() will see NULL and skip waking (the signal already did). + if (sleep_data && sleep_data->wait_queue_entry) { + sleep_data->wait_queue_entry = NULL; + } + + // Free the timer. + __timer_list_dealloc(task->sleep_timer); + + // Clear the sleep_timer reference in the task. + task->sleep_timer = NULL; + + // Free the sleep data. + if (sleep_data) { + __sleep_data_dealloc(sleep_data); + } + + pr_debug("cancel_sleep_timer: canceled for process (pid: %d)\n", task->pid); + return 0; +} + /// @brief Callback for when a sleep timer expires. /// @param data Custom data stored in the timer. static inline void sleep_timeout(unsigned long data) @@ -570,21 +614,28 @@ static inline void sleep_timeout(unsigned long data) sleep_data_t *sleep_data = (sleep_data_t *)data; // Get the wait_queue_entry. wait_queue_entry_t *wait_queue_entry = sleep_data->wait_queue_entry; - // Executed entry's wakeup test function - if (wait_queue_entry->func(wait_queue_entry, TASK_RUNNING, 0) == 1) { - pr_debug("Process (pid: %d) restored from sleep\n", wait_queue_entry->task->pid); - // Removes entry from list and memory. - remove_wait_queue(&sleep_queue, wait_queue_entry); - // Clear wait queue tracking and re-enqueue if the task is currently not runnable. - wait_queue_entry->task->waiting_on = NULL; - if (list_head_empty(&wait_queue_entry->task->run_list)) { - scheduler_enqueue_task(wait_queue_entry->task); - } - // Free the memory of the wait queue item. - wait_queue_entry_dealloc(wait_queue_entry); - // Free the memory of the sleep_data. + // Check if entry is still valid (not already freed by signal). + if (!wait_queue_entry || !wait_queue_entry->task) { + pr_debug("sleep_timeout: wait_queue_entry is NULL (already woken by signal)\n"); __sleep_data_dealloc(sleep_data); + return; } + + task_struct *task = wait_queue_entry->task; + pr_debug("sleep_timeout: timer expired for process (pid: %d)\n", task->pid); + + // Clear the sleep_timer reference in the task before waking. + task->sleep_timer = NULL; + + // Remove from the sleep queue (this subsystem owns the queue entry). + remove_wait_queue(&sleep_queue, wait_queue_entry); + + // Call centralized scheduler function to handle state and enqueue. + wake_up_process(task); + + // Free the memory of the wait queue item and sleep_data. + wait_queue_entry_dealloc(wait_queue_entry); + __sleep_data_dealloc(sleep_data); } /// @brief Function executed when the real_timer of a process expires, sends @@ -634,6 +685,9 @@ int sys_nanosleep(const struct timespec *req, struct timespec *rem) // We need to store rem somewhere, because it contains how much time left // until the timer expires, when the timer is stopped early by a signal. pr_debug("sys_nanosleep([s:%d; ns:%d],...)\n", req->tv_sec, req->tv_nsec); + // Get the current task. + task_struct *current = scheduler_get_current_process(); + assert(current && "No current process in sys_nanosleep"); // Create a dinamic timer to wake up the process after some time struct timer_list *sleep_timer = __timer_list_alloc(); // First, we save the remaining time. Then, we remove the current process @@ -647,6 +701,8 @@ int sys_nanosleep(const struct timespec *req, struct timespec *rem) sleep_timer->expires = timer_get_ticks() + __timespec_to_ticks(req); sleep_timer->function = &sleep_timeout; sleep_timer->data = (unsigned long)sleep_data; + // Store the timer in the task so signals can cancel it if needed. + current->sleep_timer = sleep_timer; // Add the timer. add_timer(sleep_timer); return 0; diff --git a/kernel/src/process/scheduler.c b/kernel/src/process/scheduler.c index f93cbba3..39561865 100644 --- a/kernel/src/process/scheduler.c +++ b/kernel/src/process/scheduler.c @@ -4,10 +4,10 @@ /// See LICENSE.md for details. // Setup the logging for this file (do this before any other include). -#include "sys/kernel_levels.h" // Include kernel log levels. -#define __DEBUG_HEADER__ "[SCHED ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. -#include "io/debug.h" // Include debugging functions. +#include "sys/kernel_levels.h" // Include kernel log levels. +#define __DEBUG_HEADER__ "[SCHED ]" ///< Change header. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#include "io/debug.h" // Include debugging functions. #include "assert.h" #include "descriptor_tables/tss.h" @@ -150,6 +150,50 @@ void scheduler_dequeue_task(task_struct *process) #endif } +int wake_up_process(task_struct *task) +{ + /// Handles waking up a task that was blocked or stopped. + /// This is the single centralized point where a task transitions + /// to TASK_RUNNING and gets enqueued to the runqueue. + /// + /// Key design: This function does NOT search wait queues - it only + /// manages task state and runqueue placement. The subsystem that put + /// the task on a wait queue (pipes, signals, timers) is responsible for + /// removing it from that queue and then calling this function. + /// + /// With boundary-based context switching (no in-kernel preemption), + /// we don't need to do immediate scheduling - the next interrupt/exception + /// boundary will call scheduler_run() to pick the next task. + /// + /// @param task The task to wake up. + /// @return 0 on success, -1 if task was already runnable. + + if (!task) { + pr_err("wake_up_process: task is NULL\n"); + return -1; + } + + // If already running, nothing to do. + if (task->state == TASK_RUNNING) { + pr_debug("wake_up_process: task %d is already TASK_RUNNING\n", task->pid); + return -1; + } + + // Set the task to runnable state. + task->state = TASK_RUNNING; + + // Clear wait queue tracking since we're waking up. + task->waiting_on = NULL; + + // If not on the runqueue, enqueue it. + if (list_head_empty(&task->run_list)) { + scheduler_enqueue_task(task); + pr_debug("wake_up_process: enqueued task %d to runqueue\n", task->pid); + } + + return 0; +} + void scheduler_run(pt_regs_t *f) { // Check if there is a running process. @@ -241,7 +285,20 @@ void scheduler_run(pt_regs_t *f) scheduler_restore_context(next, f); } } else { - pr_warning("SCHEDULER_RUN: Signal handler requested context switch, but signal handling is not yet implemented. Ignoring.\n"); + // Signal handling may have changed task state (e.g., stop/exit). + // If current is no longer runnable or queued, pick another task now. + if ((runqueue.curr->state != TASK_RUNNING) || list_head_empty(&runqueue.curr->run_list)) { + next = scheduler_pick_next_task(&runqueue); + while (!next) { + sti(); + __asm__ __volatile__("hlt"); + cli(); + next = scheduler_pick_next_task(&runqueue); + } + if (next != runqueue.curr) { + scheduler_restore_context(next, f); + } + } } //========================================================================== } diff --git a/kernel/src/process/wait.c b/kernel/src/process/wait.c index 482472d0..2bb4f86d 100644 --- a/kernel/src/process/wait.c +++ b/kernel/src/process/wait.c @@ -4,10 +4,10 @@ /// See LICENSE.md for details. // Setup the logging for this file (do this before any other include). -#include "sys/kernel_levels.h" // Include kernel log levels. -#define __DEBUG_HEADER__ "[WAIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. -#include "io/debug.h" // Include debugging functions. +#include "sys/kernel_levels.h" // Include kernel log levels. +#define __DEBUG_HEADER__ "[WAIT ]" ///< Change header. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#include "io/debug.h" // Include debugging functions. #include "process/wait.h" @@ -126,21 +126,24 @@ void wake_up_all(wait_queue_head_t *head) return; } - // iterate through the queue and invoke each wake function + // Iterate through the queue and wake each task. + // Each subsystem (pipes, signals, timers) owns its wait queue entries. + // This function: removes entries, calls wake_up_process() to handle scheduling. list_for_each_safe_decl(it, store, &head->task_list) { wait_queue_entry_t *entry = list_entry(it, wait_queue_entry_t, task_list); + + // Check if this task should be woken (via wake function). if (entry->func(entry, TASK_RUNNING, 0) || entry->task->state == TASK_RUNNING) { - // Debug on the output. pr_debug("Process %d (%s) WOKEN UP from %s\n", entry->task->pid, entry->task->name, head->name); - // Remove the entry from the wait queue and re-enqueue the task if it's not already running. + + // Remove the entry from the wait queue. remove_wait_queue(head, entry); - // Clear the wait queue tracking field when removing from queue - entry->task->waiting_on = NULL; - /* only enqueue if not already on runqueue */ - if (list_head_empty(&entry->task->run_list)) { - scheduler_enqueue_task(entry->task); - } + + // Call centralized scheduler function to handle state and enqueue. + wake_up_process(entry->task); + + // Clean up the entry. wait_queue_entry_dealloc(entry); } } @@ -157,24 +160,26 @@ int wake_up_process_on_queue(wait_queue_head_t *head, struct task_struct *task) return 0; } - // Search for the specific task in the wait queue + // Search for the specific task in the wait queue. list_for_each_safe_decl(it, store, &head->task_list) { wait_queue_entry_t *entry = list_entry(it, wait_queue_entry_t, task_list); - // Check if this entry corresponds to our target task + + // Check if this entry corresponds to our target task. if (entry->task == task) { - // Try to wake up the task using its wake function + // Try to wake up the task using its wake function. if (entry->func(entry, TASK_RUNNING, 0) || entry->task->state == TASK_RUNNING) { pr_debug("Process %d (%s) WOKEN UP from %s\n", entry->task->pid, entry->task->name, head->name); - // Remove the entry from the wait queue + + // Remove the entry from the wait queue. remove_wait_queue(head, entry); - // Clear the wait queue tracking field - entry->task->waiting_on = NULL; - // Re-enqueue the task if it's not already on the runqueue - if (list_head_empty(&entry->task->run_list)) { - scheduler_enqueue_task(entry->task); - } + + // Call centralized scheduler function to handle state and enqueue. + wake_up_process(entry->task); + + // Clean up the entry. wait_queue_entry_dealloc(entry); + return 1; // Successfully woken up } } @@ -245,6 +250,12 @@ wait_queue_entry_t *sleep_on(wait_queue_head_t *head) return NULL; } + // We want to avoid a race where an interrupt arrives between setting the task state to TASK_UNINTERRUPTIBLE and the + // caller adding the entry to the queue. If the wakeup occurs in that window the notification is lost and the task + // could sleep forever. The simple way to prevent this is to disable interrupts while changing the state and + // inserting the entry; IRQs are restored before returning so normal operation resumes. + uint8_t irqs = irq_disable(); + // Retrieve the current process/task. task_struct *sleeping_task = scheduler_get_current_process(); if (!sleeping_task) { @@ -252,12 +263,6 @@ wait_queue_entry_t *sleep_on(wait_queue_head_t *head) return NULL; } - // We want to avoid a race where an interrupt arrives between setting the task state to TASK_UNINTERRUPTIBLE and the - // caller adding the entry to the queue. If the wakeup occurs in that window the notification is lost and the task - // could sleep forever. The simple way to prevent this is to disable interrupts while changing the state and - // inserting the entry; IRQs are restored before returning so normal operation resumes. - uint8_t irqs = irq_disable(); - wait_queue_entry_t *entry = wait_queue_entry_alloc(); if (!entry) { pr_err("Failed to allocate memory for wait queue entry.\n"); @@ -280,10 +285,10 @@ wait_queue_entry_t *sleep_on(wait_queue_head_t *head) // Add the wait queue entry to the specified wait queue. add_wait_queue(head, entry); - /* restore interrupts before returning */ - irq_enable(irqs); - pr_debug("Process %d (%s) SLEEPS ON %s (entry %p)\n", sleeping_task->pid, sleeping_task->name, head->name, entry); + // Restore interrupts before returning. + irq_enable(irqs); + return entry; } diff --git a/kernel/src/system/signal.c b/kernel/src/system/signal.c index 5bf80fb4..6416bd4b 100644 --- a/kernel/src/system/signal.c +++ b/kernel/src/system/signal.c @@ -4,15 +4,16 @@ /// See LICENSE.md for details. // Setup the logging for this file (do this before any other include). -#include "sys/kernel_levels.h" // Include kernel log levels. -#define __DEBUG_HEADER__ "[SIGNAL]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. -#include "io/debug.h" // Include debugging functions. +#include "sys/kernel_levels.h" // Include kernel log levels. +#define __DEBUG_HEADER__ "[SIGNAL]" ///< Change header. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#include "io/debug.h" // Include debugging functions. #include "system/signal.h" #include "assert.h" #include "errno.h" +#include "hardware/timer.h" #include "klib/irqflags.h" #include "klib/stack_helper.h" #include "process/process.h" @@ -687,18 +688,18 @@ void handle_stop_signal(int sig, siginfo_t *info, struct task_struct *p) { wait_queue_entry_t *entry = list_entry(it, wait_queue_entry_t, task_list); - // Select only the waiting entry for the timer task pid. + // Select only the waiting entry for the target task pid. if (entry->task->pid == p->pid) { - // Executed entry's wakeup test function - if (entry->func(entry, TASK_RUNNING, 0)) { - // Removes entry from list and memory - remove_wait_queue(&stopped_queue, entry); - // Free its memory. - wait_queue_entry_dealloc(entry); - pr_debug("Restored process (%d) from stop.\n", p->pid); - } else { - pr_err("Failed to restore process (%d) from stop.\n", p->pid); - } + pr_debug("SIGCONT: waking up stopped process %d\n", p->pid); + + // Remove from the stopped queue (this subsystem owns the entry). + remove_wait_queue(&stopped_queue, entry); + + // Call centralized scheduler function to handle state and enqueue. + wake_up_process(entry->task); + + // Clean up the wait queue entry. + wait_queue_entry_dealloc(entry); break; } } @@ -770,52 +771,6 @@ int sys_kill(pid_t pid, int sig) info.si_band = 0; int ret = __send_sig_info(sig, &info, process); - // If the target process is sleeping on a wait queue, remove it from the - // queue and re-enqueue it to the runqueue so the scheduler will run it - // and handle the signal. - if ((process->state == TASK_UNINTERRUPTIBLE || process->state == TASK_INTERRUPTIBLE) && - process->waiting_on != NULL) { - // The task is sleeping on a wait queue. We need to find and remove - // the entry from the wait queue, then move it to the runqueue. - wait_queue_head_t *wait_queue = process->waiting_on; - wait_queue_entry_t *entry = NULL; - - // Acquire the wait queue lock and search for the entry. - spinlock_lock(&wait_queue->lock); - list_for_each_decl (it, &wait_queue->task_list) { - wait_queue_entry_t *candidate = list_entry(it, wait_queue_entry_t, task_list); - if (candidate->task == process) { - entry = candidate; - break; - } - } - - // If we found the entry, remove it from the wait queue. - if (entry) { - list_head_remove(&entry->task_list); - process->waiting_on = NULL; - spinlock_unlock(&wait_queue->lock); - - // Now re-enqueue the task to the runqueue if not already queued. - process->state = TASK_RUNNING; - if (list_head_empty(&process->run_list)) { - scheduler_enqueue_task(process); - } - wait_queue_entry_dealloc(entry); - pr_debug("sys_kill: removed task %d from wait queue %p and re-enqueued\n", process->pid, wait_queue); - } else { - spinlock_unlock(&wait_queue->lock); - pr_warning("sys_kill: task %d claims to sleep on wait queue %p but not found\n", process->pid, wait_queue); - } - } else if (process->state == TASK_UNINTERRUPTIBLE || process->state == TASK_INTERRUPTIBLE) { - // Task is sleeping but not on a wait queue (shouldn't happen). - if (list_head_empty(&process->run_list)) { - process->state = TASK_RUNNING; - scheduler_enqueue_task(process); - pr_debug("sys_kill: re-enqueued sleeping task %d (not on wait queue)\n", process->pid); - } - } - return ret; } diff --git a/userspace/tests/t_kill.c b/userspace/tests/t_kill.c index be733b45..ea4a3f6b 100644 --- a/userspace/tests/t_kill.c +++ b/userspace/tests/t_kill.c @@ -42,19 +42,21 @@ int main(int argc, char *argv[]) memset(&action, 0, sizeof(action)); // Clear the action structure action.sa_handler = child_sigusr1_handler; // Set handler function - // Check if setting up the signal handler fails + printf("Setting up signal handler for SIGUSR1 in child (pid: %d)...\n", cpid); + + // Check if setting up the signal handler fails. if (sigaction(SIGUSR1, &action, NULL) == -1) { fprintf(STDERR_FILENO, "Failed to set signal handler for SIGUSR1: %s\n", strerror(errno)); return EXIT_FAILURE; // Return failure if handler setup fails } - // Request to sleep for 100 ms. - struct timespec req = {0, 100000000}; + // Request to sleep for 500 ms. + struct timespec req = {0, 500000000}; // Child process loop - waiting for signals while (1) { printf("I'm the child (pid: %d): I'm waiting...\n", cpid); - // Sleep for 100 ms. + // Sleep for 500 ms. nanosleep(&req, NULL); } @@ -62,19 +64,21 @@ int main(int argc, char *argv[]) // Parent process printf("I'm the parent (pid: %d)!\n", getpid()); - // Request to sleep for 500 ms. - struct timespec req = {0, 500000000}; + // Request to sleep for 1.5 seconds. + struct timespec req = {1, 500000000}; - // Sleep for 500 ms. + // Sleep for 1.5 seconds. nanosleep(&req, NULL); + printf("Sending SIGUSR1 to child (pid: %d)!\n", cpid); + // Send SIGUSR1 to the child process if (kill(cpid, SIGUSR1) == -1) { fprintf(STDERR_FILENO, "Failed to send SIGUSR1 to child: %s\n", strerror(errno)); return EXIT_FAILURE; // Return failure if signal sending fails } - // Wait before terminating the child process, sleep for 500 ms. + // Wait before terminating the child process, sleep for 1.5 seconds. nanosleep(&req, NULL); // Send SIGTERM to the child process to terminate it From 1ec4b77d97be952d29628756935021a837d53b8e Mon Sep 17 00:00:00 2001 From: "Enrico Fraccaroli (Galfurian)" Date: Fri, 6 Mar 2026 15:11:47 +0100 Subject: [PATCH 17/22] refactor(signal): stop tasks stay on runqueue, not wait queues Stopped tasks now just change state to TASK_STOPPED while remaining on the runqueue. Scheduler naturally skips non-running tasks. SIGCONT simply transitions state back to TASK_RUNNING. Eliminates unnecessary stopped_queue wait queue infrastructure. --- kernel/src/fs/pipe.c | 6 ---- kernel/src/process/wait.c | 9 ++--- kernel/src/system/signal.c | 73 +++++--------------------------------- 3 files changed, 12 insertions(+), 76 deletions(-) diff --git a/kernel/src/fs/pipe.c b/kernel/src/fs/pipe.c index 9a32cd34..6c7d7f36 100644 --- a/kernel/src/fs/pipe.c +++ b/kernel/src/fs/pipe.c @@ -498,9 +498,6 @@ int pipe_read_wake_function(wait_queue_entry_t *wait, unsigned mode, int sync) if ((pipe_info_has_data(pipe_info) > 0) || (pipe_info->writers == 0)) { // Check if the task is in an appropriate sleep state to be woken up. if ((wait->task->state == TASK_UNINTERRUPTIBLE) || (wait->task->state == TASK_STOPPED)) { - // Set the task's state to the specified wake-up mode. - wait->task->state = mode; - // Signal that the task has been woken up. pr_debug("Data available or no more writers, waking up reader %d.\n", wait->task->pid); return 1; @@ -535,9 +532,6 @@ int pipe_write_wake_function(wait_queue_entry_t *wait, unsigned mode, int sync) if (pipe_info_has_space(pipe_info) > 0) { // Only tasks in the state TASK_UNINTERRUPTIBLE or TASK_STOPPED can be woken up. if ((wait->task->state == TASK_UNINTERRUPTIBLE) || (wait->task->state == TASK_STOPPED)) { - // Set the wake-up mode for the task. - wait->task->state = mode; - // Signal that the task has been woken up. pr_debug("Space available, waking up writer %d.\n", wait->task->pid); return 1; diff --git a/kernel/src/process/wait.c b/kernel/src/process/wait.c index 2bb4f86d..8d412f0c 100644 --- a/kernel/src/process/wait.c +++ b/kernel/src/process/wait.c @@ -65,11 +65,8 @@ int default_wake_function(wait_queue_entry_t *entry, unsigned mode, int sync) } // Only wake up tasks in TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE states. if ((entry->task->state == TASK_INTERRUPTIBLE) || (entry->task->state == TASK_UNINTERRUPTIBLE)) { - // Set the task state to the specified mode. - entry->task->state = mode; - // Optionally handle sync-specific operations here if needed. - // For now, sync is unused. + pr_debug("Task %d (%s) is being woken up from default_wake_function, current state: %d\n", entry->task->pid, entry->task->name, entry->task->state); return 1; } @@ -239,7 +236,7 @@ void remove_wait_queue(wait_queue_head_t *head, wait_queue_entry_t *entry) __remove_wait_queue(head, entry); spinlock_unlock(&head->lock); - pr_debug("Removed process %d (%s) from wait queue %s (entry %p)\n", entry->task->pid, entry->task->name, head->name, entry); + pr_debug("Removed process %d (%s) from wait queue %s (state: %d)\n", entry->task->pid, entry->task->name, head->name, entry->task->state); } wait_queue_entry_t *sleep_on(wait_queue_head_t *head) @@ -285,7 +282,7 @@ wait_queue_entry_t *sleep_on(wait_queue_head_t *head) // Add the wait queue entry to the specified wait queue. add_wait_queue(head, entry); - pr_debug("Process %d (%s) SLEEPS ON %s (entry %p)\n", sleeping_task->pid, sleeping_task->name, head->name, entry); + pr_debug("Process %d (%s) SLEEPS ON %s (state: %d)\n", sleeping_task->pid, sleeping_task->name, head->name, sleeping_task->state); // Restore interrupts before returning. irq_enable(irqs); diff --git a/kernel/src/system/signal.c b/kernel/src/system/signal.c index 6416bd4b..3b06d6fb 100644 --- a/kernel/src/system/signal.c +++ b/kernel/src/system/signal.c @@ -27,13 +27,6 @@ /// SLAB caches for signal bits. static kmem_cache_t *sigqueue_cachep; -/// Contains all stopped process waiting for a continue signal -static wait_queue_head_t stopped_queue = { - .name = "stopped_queue", - .lock = SPINLOCK_INIT, - .task_list = LIST_HEAD_INIT(stopped_queue.task_list), -}; - /// @brief The list of signal names. static const char *sys_siglist[] = { "HUP", @@ -433,37 +426,6 @@ static void __rm_from_queue(sigset_t *mask, sigpending_t *q) } } -/// @brief Wakes up a task that is waiting for a signal. -/// @param entry the entry to wake up. -/// @param mode the mode to set. -/// @param sync the sync flag. -/// @return 1 on success, 0 on failure. -int stop_wake_function(wait_queue_entry_t *entry, unsigned mode, int sync) -{ - // Validate the input. - if (!entry) { - pr_err("Variable entry is NULL.\n"); - return 0; - } - if (!entry->task) { - pr_err("Variable entry->task is NULL.\n"); - return 0; - } - // Only wake up tasks in TASK_STOPPED state. - if (entry->task->state == TASK_STOPPED) { - // Set the task state to the specified mode. - entry->task->state = mode; - - // Optionally handle sync-specific operations here if needed. - // For now, sync is unused. - - return 1; - } - - // Task is not in a wakeable state. - return 0; -} - /// @brief We do not consider group stopping because for now we don't have thread groups. /// @param current the current process. /// @param f the stack frame. @@ -479,13 +441,11 @@ static void __do_signal_stop(struct task_struct *current, struct pt_regs *f, int } } - // The state is now TASK_UNINTERRUPTABLE - wait_queue_entry_t *entry = sleep_on(&stopped_queue); - entry->task->state = TASK_STOPPED; - entry->task->exit_code = signr; - entry->func = stop_wake_function; + // Mark task as stopped (stays on runqueue, scheduler will skip it). + current->state = TASK_STOPPED; + current->exit_code = signr; - // Call the scheduler. + // Call the scheduler to pick next runnable task. scheduler_run(f); } @@ -645,8 +605,6 @@ int signals_init(void) pr_emerg("Failed to allocate cache for signals.\n"); return 0; } - // Initialize wait queue. - wait_queue_head_init(&stopped_queue); return 1; } @@ -684,24 +642,11 @@ void handle_stop_signal(int sig, siginfo_t *info, struct task_struct *p) __rm_from_queue(&mask, &p->pending); - list_for_each_safe_decl(it, store, &stopped_queue.task_list) - { - wait_queue_entry_t *entry = list_entry(it, wait_queue_entry_t, task_list); - - // Select only the waiting entry for the target task pid. - if (entry->task->pid == p->pid) { - pr_debug("SIGCONT: waking up stopped process %d\n", p->pid); - - // Remove from the stopped queue (this subsystem owns the entry). - remove_wait_queue(&stopped_queue, entry); - - // Call centralized scheduler function to handle state and enqueue. - wake_up_process(entry->task); - - // Clean up the wait queue entry. - wait_queue_entry_dealloc(entry); - break; - } + // If process is stopped, transition it back to running. + // Stopped tasks stay on runqueue but scheduler skips them. + if (p->state == TASK_STOPPED) { + pr_debug("SIGCONT: resuming stopped process %d\n", p->pid); + p->state = TASK_RUNNING; } } } From e12553bceb8525e9084a1e54772e6fb82c7cff94 Mon Sep 17 00:00:00 2001 From: "Enrico Fraccaroli (Galfurian)" Date: Fri, 6 Mar 2026 16:28:26 +0100 Subject: [PATCH 18/22] fix(wait): keep sleeping tasks on runqueue and centralize wake handling --- kernel/inc/process/wait.h | 8 +++ kernel/src/fs/pipe.c | 30 +++------- kernel/src/hardware/timer.c | 10 +--- kernel/src/process/scheduler.c | 39 +++++++------ kernel/src/process/wait.c | 103 +++++++++++++++++++++------------ kernel/src/system/signal.c | 8 +-- 6 files changed, 109 insertions(+), 89 deletions(-) diff --git a/kernel/inc/process/wait.h b/kernel/inc/process/wait.h index 8388c481..ef5dfcee 100644 --- a/kernel/inc/process/wait.h +++ b/kernel/inc/process/wait.h @@ -135,3 +135,11 @@ void wake_up_all(wait_queue_head_t *head); /// @param task The specific task to wake up. /// @return 1 if the task was found and woken up, 0 otherwise. int wake_up_process_on_queue(wait_queue_head_t *head, struct task_struct *task); + +/// @brief Wake a specific wait queue entry. +/// @param head The wait queue head that owns the entry. +/// @param entry The wait queue entry to evaluate and wake. +/// @param mode Wake mode passed to the entry wake function. +/// @param sync Wake sync flag passed to the entry wake function. +/// @return 1 if the task was woken, 0 otherwise. +int wake_up_wait_queue_entry(wait_queue_head_t *head, wait_queue_entry_t *entry, unsigned mode, int sync); diff --git a/kernel/src/fs/pipe.c b/kernel/src/fs/pipe.c index 6c7d7f36..94ea5b64 100644 --- a/kernel/src/fs/pipe.c +++ b/kernel/src/fs/pipe.c @@ -496,14 +496,13 @@ int pipe_read_wake_function(wait_queue_entry_t *wait, unsigned mode, int sync) // Validate that data is available in the pipe for reading. if ((pipe_info_has_data(pipe_info) > 0) || (pipe_info->writers == 0)) { - // Check if the task is in an appropriate sleep state to be woken up. - if ((wait->task->state == TASK_UNINTERRUPTIBLE) || (wait->task->state == TASK_STOPPED)) { - // Signal that the task has been woken up. + // Check if the task is in uninterruptible sleep state (the state it + // entered when starting the pipe read operation). + if (wait->task->state == TASK_UNINTERRUPTIBLE) { pr_debug("Data available or no more writers, waking up reader %d.\n", wait->task->pid); return 1; } pr_debug("Reader %d not in the correct state for wake-up.\n", wait->task->pid); - } else { pr_debug("No data available, reader %d should continue waiting.\n", wait->task->pid); } @@ -530,14 +529,13 @@ int pipe_write_wake_function(wait_queue_entry_t *wait, unsigned mode, int sync) // Check if there is available space in the pipe for writing. if (pipe_info_has_space(pipe_info) > 0) { - // Only tasks in the state TASK_UNINTERRUPTIBLE or TASK_STOPPED can be woken up. - if ((wait->task->state == TASK_UNINTERRUPTIBLE) || (wait->task->state == TASK_STOPPED)) { - // Signal that the task has been woken up. + // Check if the task is in uninterruptible sleep state (the state it + // entered when starting the pipe write operation). + if (wait->task->state == TASK_UNINTERRUPTIBLE) { pr_debug("Space available, waking up writer %d.\n", wait->task->pid); return 1; } pr_debug("Writer %d not in the correct state for wake-up.\n", wait->task->pid); - } else { pr_debug("No space available, writer %d should continue waiting.\n", wait->task->pid); } @@ -560,20 +558,10 @@ static void pipe_wake_up_tasks(wait_queue_head_t *wait_queue, const char *debug_ list_for_each_safe_decl(it, store, &wait_queue->task_list) { wait_queue_entry_t *wait_queue_entry = list_entry(it, wait_queue_entry_t, task_list); + int target_pid = wait_queue_entry->task ? wait_queue_entry->task->pid : -1; - // Run the wakeup test function for the waiting task. - if (wait_queue_entry->func(wait_queue_entry, TASK_RUNNING, 0)) { - // Task is ready to be woken up. - pr_debug("%s: waking up process %d\n", debug_msg, wait_queue_entry->task->pid); - - // Remove from the pipe's wait queue (this subsystem owns the entry). - remove_wait_queue(wait_queue, wait_queue_entry); - - // Call centralized scheduler function to handle state and enqueue. - wake_up_process(wait_queue_entry->task); - - // Clean up the wait queue entry. - wait_queue_entry_dealloc(wait_queue_entry); + if (wake_up_wait_queue_entry(wait_queue, wait_queue_entry, TASK_RUNNING, 0)) { + pr_debug("%s: waking up process %d\n", debug_msg, target_pid); } } } diff --git a/kernel/src/hardware/timer.c b/kernel/src/hardware/timer.c index b90533eb..4cf02085 100644 --- a/kernel/src/hardware/timer.c +++ b/kernel/src/hardware/timer.c @@ -627,14 +627,10 @@ static inline void sleep_timeout(unsigned long data) // Clear the sleep_timer reference in the task before waking. task->sleep_timer = NULL; - // Remove from the sleep queue (this subsystem owns the queue entry). - remove_wait_queue(&sleep_queue, wait_queue_entry); + // Delegate full wake mechanics to wait.c (wake check + remove + enqueue + free). + wake_up_wait_queue_entry(&sleep_queue, wait_queue_entry, TASK_RUNNING, 0); - // Call centralized scheduler function to handle state and enqueue. - wake_up_process(task); - - // Free the memory of the wait queue item and sleep_data. - wait_queue_entry_dealloc(wait_queue_entry); + // Free sleep_data owned by the timer callback. __sleep_data_dealloc(sleep_data); } diff --git a/kernel/src/process/scheduler.c b/kernel/src/process/scheduler.c index 39561865..689a9383 100644 --- a/kernel/src/process/scheduler.c +++ b/kernel/src/process/scheduler.c @@ -152,21 +152,21 @@ void scheduler_dequeue_task(task_struct *process) int wake_up_process(task_struct *task) { - /// Handles waking up a task that was blocked or stopped. - /// This is the single centralized point where a task transitions - /// to TASK_RUNNING and gets enqueued to the runqueue. + /// Wakes up a blocked or stopped task by transitioning it to TASK_RUNNING. /// - /// Key design: This function does NOT search wait queues - it only - /// manages task state and runqueue placement. The subsystem that put - /// the task on a wait queue (pipes, signals, timers) is responsible for - /// removing it from that queue and then calling this function. + /// Architecture: Tasks remain on the runqueue even when blocked. The + /// scheduler naturally skips non-TASK_RUNNING tasks when picking next. + /// This function only manages task state transitions, NOT queue membership. /// - /// With boundary-based context switching (no in-kernel preemption), - /// we don't need to do immediate scheduling - the next interrupt/exception - /// boundary will call scheduler_run() to pick the next task. + /// The wait queue layer (wait.c) is responsible for: + /// - Removing task from wait queue before calling this + /// - Freeing wait queue entry after this returns /// - /// @param task The task to wake up. - /// @return 0 on success, -1 if task was already runnable. + /// With boundary-based scheduling, the task becomes eligible at the + /// next interrupt/exception boundary when scheduler_run() executes. + /// + /// @param task The task to wake up (must be on runqueue). + /// @return 0 on success, -1 if task was already TASK_RUNNING. if (!task) { pr_err("wake_up_process: task is NULL\n"); @@ -179,16 +179,17 @@ int wake_up_process(task_struct *task) return -1; } - // Set the task to runnable state. + // Transition task to runnable state. task->state = TASK_RUNNING; - // Clear wait queue tracking since we're waking up. + // Clear wait queue tracking. task->waiting_on = NULL; - // If not on the runqueue, enqueue it. + // Safety check: task should already be on runqueue. Only enqueue if + // somehow it was removed (e.g., during process creation). if (list_head_empty(&task->run_list)) { + pr_warning("wake_up_process: task %d not on runqueue, re-enqueueing\n", task->pid); scheduler_enqueue_task(task); - pr_debug("wake_up_process: enqueued task %d to runqueue\n", task->pid); } return 0; @@ -711,10 +712,10 @@ pid_t sys_waitpid(pid_t pid, int *status, int options) } // Otherwise, block until a child exits (and sends SIGCHLD to wake us up). - // sleep_on() will dequeue this task and mark it uninterruptible. + // Task will remain on runqueue but in TASK_UNINTERRUPTIBLE state. // Context switch will happen when this syscall returns and syscall_handler calls scheduler_run(f). - // When woken up (by wake_up_process_on_queue in do_exit), this task will be - // re-enqueued and eventually scheduled again, returning to userspace with -EINTR. + // When woken up (by wake_up_all in do_exit), task state transitions to TASK_RUNNING, + // and eventually scheduler picks it again, returning to userspace with -EINTR. // Userspace must retry the syscall, which will then find and reap the zombie. pr_debug("Process %d (%s) sleeping in waitpid (no zombie child yet)\n", runqueue.curr->pid, runqueue.curr->name); wait_queue_entry_t *wait_entry = sleep_on(&waitpid_queue); diff --git a/kernel/src/process/wait.c b/kernel/src/process/wait.c index 8d412f0c..0c122d01 100644 --- a/kernel/src/process/wait.c +++ b/kernel/src/process/wait.c @@ -54,6 +54,14 @@ static inline void __remove_wait_queue(wait_queue_head_t *head, wait_queue_entry int default_wake_function(wait_queue_entry_t *entry, unsigned mode, int sync) { + /// Default wake predicate for wait queue entries. + /// Returns 1 if the task is in a sleep state and should be woken, + /// 0 if it should remain waiting. + /// + /// Wake functions are policy: they decide whether a task's wait + /// condition is satisfied. The wait.c layer handles mechanics: + /// removing from queue, calling wake_up_process(), freeing entry. + // Validate the input. if (!entry) { pr_err("Variable entry is NULL.\n"); @@ -63,15 +71,16 @@ int default_wake_function(wait_queue_entry_t *entry, unsigned mode, int sync) pr_err("Variable entry->task is NULL.\n"); return 0; } - // Only wake up tasks in TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE states. - if ((entry->task->state == TASK_INTERRUPTIBLE) || (entry->task->state == TASK_UNINTERRUPTIBLE)) { - - pr_debug("Task %d (%s) is being woken up from default_wake_function, current state: %d\n", entry->task->pid, entry->task->name, entry->task->state); + // Wake if task is in a sleep state (interruptible or uninterruptible). + if ((entry->task->state == TASK_INTERRUPTIBLE) || + (entry->task->state == TASK_UNINTERRUPTIBLE)) { + pr_debug("Task %d (%s) wake condition met (state: %d)\n", + entry->task->pid, entry->task->name, entry->task->state); return 1; } - // Task is not in a wakeable state. + // Task is not in a wakeable state (already running, stopped, or zombie). return 0; } @@ -116,6 +125,53 @@ void wait_queue_entry_dealloc(wait_queue_entry_t *entry) kfree(entry); } +int wake_up_wait_queue_entry(wait_queue_head_t *head, wait_queue_entry_t *entry, unsigned mode, int sync) +{ + /// Core wait queue wake primitive. Evaluates wake condition via entry's + /// wake function, and if satisfied, performs the full wake sequence: + /// 1. Remove entry from wait queue (wait layer responsibility) + /// 2. Call wake_up_process() to transition task to TASK_RUNNING + /// 3. Free wait queue entry (wait layer responsibility) + /// + /// This is the ONLY function that should perform this sequence. + /// Subsystems (pipes, timers) delegate full wake mechanics here. + + if (!head) { + pr_err("wake_up_wait_queue_entry: head is NULL\n"); + return 0; + } + if (!entry) { + pr_err("wake_up_wait_queue_entry: entry is NULL\n"); + return 0; + } + if (!entry->task) { + pr_err("wake_up_wait_queue_entry: entry->task is NULL\n"); + return 0; + } + + // Evaluate wake condition via function pointer (policy decision). + int should_wake = 0; + if (entry->func) { + should_wake = entry->func(entry, mode, sync); + } else { + should_wake = default_wake_function(entry, mode, sync); + } + + // Also wake if task is already TASK_RUNNING (race or redundant wake). + if (!should_wake && (entry->task->state != TASK_RUNNING)) { + return 0; + } + + pr_debug("Process %d (%s) WOKEN UP from %s\n", + entry->task->pid, entry->task->name, head->name); + + // Perform wake sequence: remove, wake, free (wait layer mechanics). + remove_wait_queue(head, entry); + wake_up_process(entry->task); + wait_queue_entry_dealloc(entry); + return 1; +} + void wake_up_all(wait_queue_head_t *head) { if (!head) { @@ -129,20 +185,7 @@ void wake_up_all(wait_queue_head_t *head) list_for_each_safe_decl(it, store, &head->task_list) { wait_queue_entry_t *entry = list_entry(it, wait_queue_entry_t, task_list); - - // Check if this task should be woken (via wake function). - if (entry->func(entry, TASK_RUNNING, 0) || entry->task->state == TASK_RUNNING) { - pr_debug("Process %d (%s) WOKEN UP from %s\n", entry->task->pid, entry->task->name, head->name); - - // Remove the entry from the wait queue. - remove_wait_queue(head, entry); - - // Call centralized scheduler function to handle state and enqueue. - wake_up_process(entry->task); - - // Clean up the entry. - wait_queue_entry_dealloc(entry); - } + wake_up_wait_queue_entry(head, entry, TASK_RUNNING, 0); } } @@ -164,21 +207,7 @@ int wake_up_process_on_queue(wait_queue_head_t *head, struct task_struct *task) // Check if this entry corresponds to our target task. if (entry->task == task) { - // Try to wake up the task using its wake function. - if (entry->func(entry, TASK_RUNNING, 0) || entry->task->state == TASK_RUNNING) { - pr_debug("Process %d (%s) WOKEN UP from %s\n", entry->task->pid, entry->task->name, head->name); - - // Remove the entry from the wait queue. - remove_wait_queue(head, entry); - - // Call centralized scheduler function to handle state and enqueue. - wake_up_process(entry->task); - - // Clean up the entry. - wait_queue_entry_dealloc(entry); - - return 1; // Successfully woken up - } + return wake_up_wait_queue_entry(head, entry, TASK_RUNNING, 0); } } @@ -268,21 +297,19 @@ wait_queue_entry_t *sleep_on(wait_queue_head_t *head) } // Set the task state to uninterruptible to indicate it is sleeping. + // Task remains on runqueue - scheduler will skip it when picking next task. sleeping_task->state = TASK_UNINTERRUPTIBLE; // Track which wait queue this task is sleeping on. sleeping_task->waiting_on = head; - // Remove task from runqueue so scheduler will ignore it while blocked. - scheduler_dequeue_task(sleeping_task); - // Initialize the wait queue entry with the current task. wait_queue_entry_init(entry, sleeping_task); // Add the wait queue entry to the specified wait queue. add_wait_queue(head, entry); - pr_debug("Process %d (%s) SLEEPS ON %s (state: %d)\n", sleeping_task->pid, sleeping_task->name, head->name, sleeping_task->state); + pr_debug("Process %d (%s) SLEEPS ON %s (state: %d, stays on runqueue)\n", sleeping_task->pid, sleeping_task->name, head->name, sleeping_task->state); // Restore interrupts before returning. irq_enable(irqs); diff --git a/kernel/src/system/signal.c b/kernel/src/system/signal.c index 3b06d6fb..29a05352 100644 --- a/kernel/src/system/signal.c +++ b/kernel/src/system/signal.c @@ -4,10 +4,10 @@ /// See LICENSE.md for details. // Setup the logging for this file (do this before any other include). -#include "sys/kernel_levels.h" // Include kernel log levels. -#define __DEBUG_HEADER__ "[SIGNAL]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. -#include "io/debug.h" // Include debugging functions. +#include "sys/kernel_levels.h" // Include kernel log levels. +#define __DEBUG_HEADER__ "[SIGNAL]" ///< Change header. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#include "io/debug.h" // Include debugging functions. #include "system/signal.h" From 3cad24ed52bda08ce9f5caa4bfba711d16ba66b1 Mon Sep 17 00:00:00 2001 From: "Enrico Fraccaroli (Galfurian)" Date: Fri, 6 Mar 2026 16:41:40 +0100 Subject: [PATCH 19/22] fix(timer): close nanosleep sleep-arm race --- kernel/src/hardware/timer.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/kernel/src/hardware/timer.c b/kernel/src/hardware/timer.c index 4cf02085..e4640ec2 100644 --- a/kernel/src/hardware/timer.c +++ b/kernel/src/hardware/timer.c @@ -684,6 +684,10 @@ int sys_nanosleep(const struct timespec *req, struct timespec *rem) // Get the current task. task_struct *current = scheduler_get_current_process(); assert(current && "No current process in sys_nanosleep"); + // Prevent a race between entering sleep state and arming the wake timer. + // If a timer interrupt preempts in that window, the task can become + // TASK_UNINTERRUPTIBLE without a wake source and block the system. + uint8_t irqs = irq_disable(); // Create a dinamic timer to wake up the process after some time struct timer_list *sleep_timer = __timer_list_alloc(); // First, we save the remaining time. Then, we remove the current process @@ -701,6 +705,8 @@ int sys_nanosleep(const struct timespec *req, struct timespec *rem) current->sleep_timer = sleep_timer; // Add the timer. add_timer(sleep_timer); + // Sleep state + wait queue entry + timer arm are now atomically visible. + irq_enable(irqs); return 0; } From 95a498fff54ba843e40c460744a6e1c024410470 Mon Sep 17 00:00:00 2001 From: "Enrico Fraccaroli (Galfurian)" Date: Fri, 6 Mar 2026 16:59:27 +0100 Subject: [PATCH 20/22] fix(signal): wake interruptible sleepers for pending signals --- kernel/inc/hardware/timer.h | 5 +++++ kernel/inc/process/wait.h | 8 ++++++++ kernel/src/hardware/timer.c | 2 +- kernel/src/process/wait.c | 16 +++++++++++++--- kernel/src/system/signal.c | 14 ++++++++++++++ 5 files changed, 41 insertions(+), 4 deletions(-) diff --git a/kernel/inc/hardware/timer.h b/kernel/inc/hardware/timer.h index a3141032..a211b624 100644 --- a/kernel/inc/hardware/timer.h +++ b/kernel/inc/hardware/timer.h @@ -156,6 +156,11 @@ void remove_timer(struct timer_list *timer); /// the process. int sys_nanosleep(const struct timespec *req, struct timespec *rem); +/// @brief Cancels nanosleep timer for a task interrupted by a signal. +/// @param task The task whose sleep timer should be canceled. +/// @return 0 on success, -1 if no sleep timer exists. +int cancel_sleep_timer(struct task_struct *task); + /// @brief Send signal to calling thread after desired seconds. /// @param seconds The number of seconds in the interval /// @return the number of seconds remaining until any previously scheduled diff --git a/kernel/inc/process/wait.h b/kernel/inc/process/wait.h index ef5dfcee..81e9a419 100644 --- a/kernel/inc/process/wait.h +++ b/kernel/inc/process/wait.h @@ -123,6 +123,14 @@ int default_wake_function(wait_queue_entry_t *entry, unsigned mode, int sync); /// sleeping process. wait_queue_entry_t *sleep_on(wait_queue_head_t *head); +/// @brief Sets the state of the current process to TASK_INTERRUPTIBLE +/// and inserts it into the specified wait queue. +/// +/// @param head Waitqueue where to sleep. +/// @return Pointer to the entry inside the wq representing the +/// sleeping process. +wait_queue_entry_t *sleep_on_interruptible(wait_queue_head_t *head); + /// @brief Wake all tasks waiting on the queue. /// /// Calls each entry's wake function and removes entries whose function diff --git a/kernel/src/hardware/timer.c b/kernel/src/hardware/timer.c index e4640ec2..740565a8 100644 --- a/kernel/src/hardware/timer.c +++ b/kernel/src/hardware/timer.c @@ -696,7 +696,7 @@ int sys_nanosleep(const struct timespec *req, struct timespec *rem) // req and rem pointers (?) sleep_data_t *sleep_data = __sleep_data_alloc(); sleep_data->remaining = rem; - sleep_data->wait_queue_entry = sleep_on(&sleep_queue); + sleep_data->wait_queue_entry = sleep_on_interruptible(&sleep_queue); // Setup the timer. sleep_timer->expires = timer_get_ticks() + __timespec_to_ticks(req); sleep_timer->function = &sleep_timeout; diff --git a/kernel/src/process/wait.c b/kernel/src/process/wait.c index 0c122d01..a2a129fd 100644 --- a/kernel/src/process/wait.c +++ b/kernel/src/process/wait.c @@ -268,7 +268,7 @@ void remove_wait_queue(wait_queue_head_t *head, wait_queue_entry_t *entry) pr_debug("Removed process %d (%s) from wait queue %s (state: %d)\n", entry->task->pid, entry->task->name, head->name, entry->task->state); } -wait_queue_entry_t *sleep_on(wait_queue_head_t *head) +static wait_queue_entry_t *__sleep_on_state(wait_queue_head_t *head, int state) { // Validate input parameters. if (!head) { @@ -296,9 +296,9 @@ wait_queue_entry_t *sleep_on(wait_queue_head_t *head) return NULL; } - // Set the task state to uninterruptible to indicate it is sleeping. + // Set the task state to indicate it is sleeping. // Task remains on runqueue - scheduler will skip it when picking next task. - sleeping_task->state = TASK_UNINTERRUPTIBLE; + sleeping_task->state = state; // Track which wait queue this task is sleeping on. sleeping_task->waiting_on = head; @@ -316,3 +316,13 @@ wait_queue_entry_t *sleep_on(wait_queue_head_t *head) return entry; } + +wait_queue_entry_t *sleep_on(wait_queue_head_t *head) +{ + return __sleep_on_state(head, TASK_UNINTERRUPTIBLE); +} + +wait_queue_entry_t *sleep_on_interruptible(wait_queue_head_t *head) +{ + return __sleep_on_state(head, TASK_INTERRUPTIBLE); +} diff --git a/kernel/src/system/signal.c b/kernel/src/system/signal.c index 29a05352..16f35415 100644 --- a/kernel/src/system/signal.c +++ b/kernel/src/system/signal.c @@ -196,6 +196,20 @@ static int __send_signal(int sig, siginfo_t *info, struct task_struct *t) pr_debug( "Added pending signal (%2d:%s) to task (%2d:%s), pending `%d, %d`.\n", sig, strsignal(sig), t->pid, t->name, t->pending.signal.sig[0], t->pending.signal.sig[1]); + + // If the task is in an interruptible sleep, wake it so the signal can be + // processed at the next return-to-user boundary. + if ((t->state == TASK_INTERRUPTIBLE) && (t->waiting_on != NULL)) { + // If task is sleeping in nanosleep, cancel its timer before waking. + if (t->sleep_timer != NULL) { + cancel_sleep_timer(t); + } + + if (!wake_up_process_on_queue(t->waiting_on, t)) { + wake_up_process(t); + } + } + __unlock_task_sighand(t); return 0; } From 5893988ed925c9c6afe8bfc3d63ece0cb5c8c7ee Mon Sep 17 00:00:00 2001 From: "Enrico Fraccaroli (Galfurian)" Date: Fri, 6 Mar 2026 17:07:07 +0100 Subject: [PATCH 21/22] fix(waitpid): avoid double dequeue on zombie reap --- kernel/src/process/scheduler.c | 19 +++++++++----- kernel/src/process/wait.c | 2 +- userspace/tests/t_semget.c | 47 +++++++++++++++++----------------- 3 files changed, 38 insertions(+), 30 deletions(-) diff --git a/kernel/src/process/scheduler.c b/kernel/src/process/scheduler.c index 689a9383..ccd82bd3 100644 --- a/kernel/src/process/scheduler.c +++ b/kernel/src/process/scheduler.c @@ -4,10 +4,10 @@ /// See LICENSE.md for details. // Setup the logging for this file (do this before any other include). -#include "sys/kernel_levels.h" // Include kernel log levels. -#define __DEBUG_HEADER__ "[SCHED ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. -#include "io/debug.h" // Include debugging functions. +#include "sys/kernel_levels.h" // Include kernel log levels. +#define __DEBUG_HEADER__ "[SCHED ]" ///< Change header. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#include "io/debug.h" // Include debugging functions. #include "assert.h" #include "descriptor_tables/tss.h" @@ -696,8 +696,15 @@ pid_t sys_waitpid(pid_t pid, int *status, int options) pid_manager_mark_free(child->pid); // Free the PID. vfs_destroy_task(child); // Finalize VFS structures. list_head_remove(&child->sibling); // Remove from parent's child list. - scheduler_dequeue_task(child); // Remove from the scheduler. - kmem_cache_free(child); // Free the `task_struct`. + + // Zombie tasks are typically dequeued in scheduler_run() when they + // become current. During waitpid() reap they may already be out of + // the runqueue, so only dequeue if still linked. + if (!list_head_empty(&child->run_list)) { + scheduler_dequeue_task(child); + } + + kmem_cache_free(child); // Free the `task_struct`. pr_debug("Process %d (%s) CLEANED UP process %d.\n", runqueue.curr->pid, runqueue.curr->name, child_pid); diff --git a/kernel/src/process/wait.c b/kernel/src/process/wait.c index a2a129fd..63d81def 100644 --- a/kernel/src/process/wait.c +++ b/kernel/src/process/wait.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[WAIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "process/wait.h" diff --git a/userspace/tests/t_semget.c b/userspace/tests/t_semget.c index 2158cdc8..aa0ff869 100644 --- a/userspace/tests/t_semget.c +++ b/userspace/tests/t_semget.c @@ -15,6 +15,7 @@ #include #include #include +#include #include int main(int argc, char *argv[]) @@ -30,38 +31,38 @@ int main(int argc, char *argv[]) // Generate a unique key using ftok. key = ftok("/", 5); if (key < 0) { - perror("Failed to generate key using ftok"); + syslog(LOG_ERR, "[t_semget] Failed to generate key using ftok: %s\n", strerror(errno)); return 1; } - printf("Generated key using ftok (key = %d)\n", key); + syslog(LOG_INFO, "[t_semget] Generated key using ftok (key = %d)\n", key); // ======================================================================== // Create a semaphore set with one semaphore. semid = semget(key, 1, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (semid < 0) { - perror("Failed to create semaphore set"); + syslog(LOG_ERR, "[t_semget] Failed to create semaphore set: %s\n", strerror(errno)); return 1; } - printf("[father] Created semaphore set (id : %ld)\n", semid); + syslog(LOG_INFO, "[t_semget][father] Created semaphore set (id : %ld)\n", semid); // ======================================================================== // Set the value of the semaphore to 1. arg.val = 1; ret = semctl(semid, 0, SETVAL, &arg); if (ret < 0) { - perror("Failed to set semaphore value"); + syslog(LOG_ERR, "[t_semget] Failed to set semaphore value: %s\n", strerror(errno)); return 1; } - printf("[father] Set semaphore value to 1 (id : %ld)\n", semid); + syslog(LOG_INFO, "[t_semget][father] Set semaphore value to 1 (id : %ld)\n", semid); // ======================================================================== // Verify that the semaphore value is set correctly. ret = semctl(semid, 0, GETVAL, NULL); if (ret < 0) { - perror("Failed to get semaphore value"); + syslog(LOG_ERR, "[t_semget] Failed to get semaphore value: %s\n", strerror(errno)); return 1; } - printf("[father] Semaphore value is %ld (expected: 1)\n", ret); + syslog(LOG_INFO, "[t_semget][father] Semaphore value is %ld (expected: 1)\n", ret); // ======================================================================== // Fork a child process. @@ -77,42 +78,42 @@ int main(int argc, char *argv[]) // Increment the semaphore, unblocking the parent. if (semop(semid, &op_child, 1) < 0) { - perror("Failed to perform child semaphore operation"); + syslog(LOG_ERR, "[t_semget][child] Failed to perform child semaphore operation: %s\n", strerror(errno)); return 1; } - printf("[child] Performed first semaphore operation (id: %ld)\n", semid); + syslog(LOG_INFO, "[t_semget][child] Performed first semaphore operation (id: %ld)\n", semid); // Verify the updated semaphore value. ret = semctl(semid, 0, GETVAL, NULL); if (ret < 0) { - perror("Failed to get semaphore value in child"); + syslog(LOG_ERR, "[t_semget][child] Failed to get semaphore value in child: %s\n", strerror(errno)); return 1; } - printf("[child] Semaphore value is %ld (expected: 2)\n", ret); + syslog(LOG_INFO, "[t_semget][child] Semaphore value after first increment is %ld (concurrent parent may consume immediately)\n", ret); // Sleep and perform another increment operation. nanosleep(&req, NULL); if (semop(semid, &op_child, 1) < 0) { - perror("Failed to perform second child semaphore operation"); + syslog(LOG_ERR, "[t_semget][child] Failed to perform second child semaphore operation: %s\n", strerror(errno)); return 1; } - printf("[child] Performed second semaphore operation (id: %ld)\n", semid); + syslog(LOG_INFO, "[t_semget][child] Performed second semaphore operation (id: %ld)\n", semid); // Check final semaphore value. ret = semctl(semid, 0, GETVAL, NULL); if (ret < 0) { - perror("Failed to get final semaphore value in child"); + syslog(LOG_ERR, "[t_semget][child] Failed to get final semaphore value in child: %s\n", strerror(errno)); return 1; } - printf("[child] Final semaphore value is %ld\n", ret); + syslog(LOG_INFO, "[t_semget][child] Final semaphore value is %ld\n", ret); // Delete the semaphore set. ret = semctl(semid, 0, IPC_RMID, 0); if (ret < 0) { - perror("Failed to remove semaphore set in child"); + syslog(LOG_ERR, "[t_semget][child] Failed to remove semaphore set in child: %s\n", strerror(errno)); return 1; } - printf("[child] Removed semaphore set (id: %ld)\n", semid); + syslog(LOG_INFO, "[t_semget][child] Removed semaphore set (id: %ld)\n", semid); // Exit the child process. return 0; @@ -135,22 +136,22 @@ int main(int argc, char *argv[]) // ======================================================================== // Perform the blocking semaphore operations. if (semop(semid, op_father, 3) < 0) { - perror("Failed to perform parent semaphore operations"); + syslog(LOG_ERR, "[t_semget][father] Failed to perform parent semaphore operations: %s\n", strerror(errno)); return 1; } - printf("[father] Performed semaphore operations (id: %ld)\n", semid); + syslog(LOG_INFO, "[t_semget][father] Performed semaphore operations (id: %ld)\n", semid); // Verify that the semaphore value is updated correctly. ret = semctl(semid, 0, GETVAL, NULL); if (ret < 0) { - perror("Failed to get semaphore value in parent"); + syslog(LOG_ERR, "[t_semget][father] Failed to get semaphore value in parent: %s\n", strerror(errno)); return 1; } - printf("[father] Semaphore value is %ld (expected: 0)\n", ret); + syslog(LOG_INFO, "[t_semget][father] Semaphore value is %ld (expected: 0)\n", ret); // Wait for the child process to terminate. if (wait(NULL) == -1) { - fprintf(stderr, "Failed to wait for child process: %s\n", strerror(errno)); + syslog(LOG_ERR, "[t_semget] Failed to wait for child process: %s\n", strerror(errno)); return EXIT_FAILURE; // Return failure if wait fails. } From 6b122734784b3b859d7d166a1d3edcb0bb74c5a6 Mon Sep 17 00:00:00 2001 From: "Enrico Fraccaroli (Galfurian)" Date: Fri, 6 Mar 2026 17:27:35 +0100 Subject: [PATCH 22/22] fix(signal): wake stopped tasks on SIGKILL to allow processing SIGKILL cannot be caught or ignored, so stopped tasks (TASK_STOPPED) must be woken to TASK_RUNNING state when they receive SIGKILL. Without this, a stopped process receiving kill -9 would never be scheduled to process the fatal signal. This partially addresses issue #143 by ensuring stopped tasks can be killed. SIGCONT already wakes stopped tasks via handle_stop_signal(). --- kernel/src/system/signal.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/kernel/src/system/signal.c b/kernel/src/system/signal.c index 16f35415..979a7bfc 100644 --- a/kernel/src/system/signal.c +++ b/kernel/src/system/signal.c @@ -210,6 +210,14 @@ static int __send_signal(int sig, siginfo_t *info, struct task_struct *t) } } + // SIGKILL cannot be caught or ignored, so stopped tasks must be woken + // to process it. Note that SIGCONT already wakes stopped tasks via + // handle_stop_signal() which is called before __send_signal(). + if ((sig == SIGKILL) && (t->state == TASK_STOPPED)) { + pr_debug("SIGKILL: waking stopped process %d (%s) so it can be killed\n", t->pid, t->name); + t->state = TASK_RUNNING; + } + __unlock_task_sighand(t); return 0; }