-
Notifications
You must be signed in to change notification settings - Fork 11
Support SMP scheduling and synchronization #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
visitorckw
wants to merge
18
commits into
sysprog21:main
Choose a base branch
from
visitorckw:support-smp
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+365
−229
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Introduce a simple spinlock implementation based on test-and-set using RV32A atomic instructions. The spinlock API includes basic locking, IRQ-safe variants, and versions that save and restore interrupt state. To support atomic instructions, the Makefile is updated to enable the 'A' extension by changing the -march flag. This is the first step toward enabling multi-core task scheduling support on RISC-V SMP systems.
The original malloc/free implementation used CRITICAL_ENTER() and CRITICAL_LEAVE() to protect critical sections by simply disabling interrupts, which is sufficient on single-core systems. To support SMP, we replace these with a proper spinlock that uses RV32A atomic instructions. This ensures correctness when multiple harts access the allocator concurrently. This change allows future task scheduling across multiple harts without risking race conditions in the memory allocator.
The original message queue implementation used CRITICAL_ENTER() and CRITICAL_LEAVE() to protect critical sections by disabling interrupts. This was sufficient for single-core systems, where only one hart could execute tasks. To support SMP, we replace these macros with a proper spinlock using RV32A atomic instructions. This ensures safe access to the internal queue structures when multiple harts concurrently interact with message queues. This change eliminates potential race conditions in message queue operations as we move toward multi-hart scheduling.
…pport The original task management code used CRITICAL_ENTER() / CRITICAL_LEAVE() and NOSCHED_ENTER() / NOSCHED_LEAVE() to protect critical sections by disabling interrupts, which was sufficient for single-core systems. To support SMP, these macros are replaced with a spinlock based on RV32A atomic instructions. This ensures that multiple harts can safely access and modify shared task data such as ready queues, priority values, and task control blocks. This change is essential for enabling multi-hart task scheduling without introducing race conditions in the kernel task subsystem.
The original pipe implementation used CRITICAL_ENTER() and CRITICAL_LEAVE() to protect critical sections by disabling interrupts, which was acceptable for single-core systems. To support SMP, these macros are replaced with a proper spinlock based on RV32A atomic instructions. This ensures safe concurrent access to the circular buffer used by the pipe, even when multiple harts are performing read or write operations simultaneously. This change is necessary to avoid race conditions and ensure correct pipe behavior under multi-hart task scheduling.
The original semaphore implementation used NOSCHED_ENTER() and NOSCHED_LEAVE() to protect critical sections by disabling interrupts, which was sufficient in single-core environments. To support SMP, we replace these macros with a spinlock based on RV32A atomic instructions. This ensures safe access to shared semaphore state, including the count and wait queue, when multiple harts operate concurrently. This change is necessary to avoid race conditions during mo_sem_wait(), mo_sem_signal(), and other semaphore operations under multi-hart scheduling.
The timer subsystem originally used NOSCHED_ENTER() and NOSCHED_LEAVE() to disable interrupts when accessing shared timer state, which sufficed on single-core systems. To support SMP, we now replace these macros with a spinlock based on RV32A atomic instructions. This ensures safe concurrent access to global timer state such as timer_initialized, the timer list, and ID management. This change prepares the timer subsystem for correct operation when multiple harts simultaneously create, start, or cancel timers.
The mutex and condition variable implementation previously relied on NOSCHED_ENTER() and NOSCHED_LEAVE() to protect critical sections by disabling interrupts. This works in single-core environments but breaks down under SMP due to race conditions between harts. This patch replaces those macros with a spinlock built using RV32A atomic instructions. The spinlock protects access to shared state including mutex ownership, waiter lists, and condition wait queues. This change ensures correct mutual exclusion and atomicity when multiple harts concurrently lock/unlock mutexes or signal condition variables.
On SMP systems, concurrent calls to printf() from multiple harts can cause interleaved and unreadable output due to racing writes to the shared output buffer. Add a spinlock to serialize access to printf(), ensuring that only one hart writes at a time. This change improves the readability of debug messages and prevents garbled output when multiple harts are active.
All calls to NOSCHED_ENTER(), NOSCHED_LEAVE(), CRITICAL_ENTER(), and CRITICAL_LEAVE() have been replaced with spinlock-based synchronization primitives throughout the kernel. As a result, these macros are no longer used and have been removed from include/sys/task.h to clean up the codebase and avoid confusion.
To support SMP, allocate separate stack memory regions for each hart during boot. This patch modifies the assembly entry code in arch/riscv/boot.c to compute the initial stack pointer based on the hart ID, ensuring each hart uses a distinct stack area of fixed size (STACK_SIZE_PER_HART). This enables multiple harts to safely run concurrently without stack collisions during early boot stages.
Remove the old logic that parks all secondary harts in WFI, which caused them to hang indefinitely. Instead, all harts proceed with boot. To ensure proper initialization sequence, hart 0 performs hardware setup, heap initialization, and task creation. Other harts spin-wait on a spinlock-protected flag until hart 0 finishes initialization before starting task dispatch.
The task_lock spinlock was primarily used to protect access to the Kernel Control Block (kcb) and its internal data structures. Move the spinlock into the kcb_t struct as kcb_lock, consolidating related state and synchronization primitives together. All uses of the standalone task_lock spinlock are replaced by kcb->kcb_lock accesses, improving code clarity and encapsulation of the kernel's core control block.
To prevent kernel panic during startup when some harts may not have any runnable tasks assigned, add an idle task for each hart. The idle task runs an infinite loop calling mo_task_wfi(), ensuring the hart remains in a low-power wait state instead of causing a panic due to lack of tasks. This guarantees that every hart has at least one task to execute immediately after boot, improving system robustness and stability on SMP setups.
Previously, only a single global pointer tracked the current running task, which worked for single-core systems. To support SMP, change the Kernel Control Block (KCB) to maintain an array of current task pointers, one per hart. Added get_task_current() and set_task_current() helper functions to retrieve and update the current task for the executing hart. Modify kernel and HAL code to use these new functions instead of the single global current task pointer, ensuring correct task tracking on each hart.
Since kcb->ticks is shared and updated by all cores, add a spinlock to protect its increment operation in the dispatcher, ensuring atomicity and preventing race conditions in SMP environments.
Previously, mtimecmp was accessed at a fixed MMIO address assuming a single core. Each hart has its own mtimecmp register at distinct offsets, so update mtimecmp read and write functions to index based on the current hart ID, ensuring correct timer compare handling in SMP systems.
Enable running the kernel on 4 simulated cores by passing the -smp 4 parameter to qemu-system-riscv32, facilitating SMP testing and development.
Marking this PR as a draft due to test failures in several applications, but I still appreciate any reviews or suggestions. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Enable SMP support by adding multi-core QEMU simulation and adapting
RISC-V boot for per-hart stacks and timers. It replaces interrupt
masking with spinlocks in core kernel subsystems for safe concurrency,
updates the kernel control block to track per-hart current tasks, and
spawns idle tasks at boot to prevent panics. Spinlock-based
synchronization replaces hart parking during boot, and printf output is
protected against interleaving. A spinlock implementation using RV32A
atomics is also included. These changes enable stable multi-core
operation.