feat(sched): add CPU core affinity via ClusteredEDF scheduler#701
Open
ytakano wants to merge 6 commits into
Open
feat(sched): add CPU core affinity via ClusteredEDF scheduler#701ytakano wants to merge 6 commits into
ytakano wants to merge 6 commits into
Conversation
Generalize the PartitionedEDF scheduler, which pinned each task to a single core, into ClusteredEDF where each task carries a CpuSet of cores it may run on. Run queues are managed by the affinity_btree_queue crate: a single affinity-aware priority queue replaces the per-core BinaryHeap array, and get_next pops the earliest-deadline task runnable on the calling CPU via pop_for_cpu. - awkernel_lib: add const-friendly CpuSet ([u64; NUM_MAX_CPU/64]) with const constructors so it can live in the const-evaluated PRIORITY_LIST. - SchedulerType::PartitionedEDF(u64, u16) -> ClusteredEDF(u64, CpuSet); Task.partitioned_core -> cpu_set: Option<CpuSet>. spawn masks out CPU 0 and out-of-range bits, falling back to all worker cores when empty. - invoke_preemption picks the core running the lowest-priority task among the set; enqueues without preemption when any core in the set is idle. - Rename partitioned symbols to clustered (PartitionedTask -> ClusteredTask, NUM_PARTITIONED_TASKS_IN_QUEUE -> NUM_CLUSTERED_TASKS_IN_QUEUE, test crate test_partitioned_edf -> test_clustered_edf). - Fix a pre-existing lost-wakeup bug in wake_workers: break -> continue so higher-numbered CPUs with queued clustered tasks are not skipped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Yuuki Takano <ytakanoster@gmail.com>
Signed-off-by: Yuuki Takano <ytakanoster@gmail.com>
Signed-off-by: Yuuki Takano <ytakanoster@gmail.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Introduces a new ClusteredEDF real-time scheduler that generalizes the prior PartitionedEDF model by allowing tasks to run on a set of worker cores (CpuSet affinity), and updates the surrounding API, tests, and documentation accordingly.
Changes:
- Add
CpuSet(const-friendly CPU affinity bitmask) and migrate task/scheduler APIs from single-core pinning to affinity sets. - Replace per-core EDF runqueues with a single affinity-aware
AffinityBTreeQueue, and add a newclustered_edfscheduler implementation. - Rename and update the userland/kernel test feature and test crate from
test_partitioned_edftotest_clustered_edf, plus doc updates/regeneration.
Reviewed changes
Copilot reviewed 18 out of 19 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| userland/src/lib.rs | Switch test entrypoint feature from test_partitioned_edf to test_clustered_edf. |
| userland/Cargo.toml | Rename optional dependency + feature flag to test_clustered_edf. |
| kernel/Cargo.toml | Rename kernel feature passthrough to test_clustered_edf. |
| awkernel_lib/src/cpu.rs | Add CpuSet type and related helpers/constants. |
| awkernel_async_lib/src/task.rs | Replace partitioned_core with cpu_set, update counters and wake logic. |
| awkernel_async_lib/src/scheduler.rs | Register ClusteredEDF, update scheduler type API, add clustered counter RAII wrapper. |
| awkernel_async_lib/src/scheduler/partitioned_edf.rs | Remove the old PartitionedEDF scheduler implementation. |
| awkernel_async_lib/src/scheduler/clustered_edf.rs | Add new ClusteredEDF scheduler implementation using AffinityBTreeQueue. |
| awkernel_async_lib/Cargo.toml | Add affinity_btree_queue dependency. |
| applications/tests/test_clustered_edf/src/lib.rs | Update tests to exercise core pinning and multi-core affinity via CpuSet. |
| applications/tests/test_clustered_edf/Cargo.toml | Rename test crate to test_clustered_edf. |
| mdbook/src/internal/scheduler.md | Update scheduler docs from PartitionedEDF to ClusteredEDF. |
| docs/print.html | Regenerated docs including new ClusteredEDF content (and additional RISC-V sections). |
| docs/internal/scheduler.html | Regenerated scheduler page reflecting ClusteredEDF. |
| docs/internal/page_table.html | Regenerated docs content (adds RISC-V sections). |
| docs/internal/memory_allocator.html | Regenerated docs content (adds RISC-V sections). |
| docs/internal/interrupt_controller.html | Regenerated docs content (adds RISC-V sections). |
| docs/internal/arch/mapper.html | Regenerated docs content (adds RISC-V sections). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Yuuki Takano <ytakanoster@gmail.com>
Signed-off-by: Yuuki Takano <ytakanoster@gmail.com>
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
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.
Description
Generalizes the
PartitionedEDFscheduler — which pinned each task to a single core — into aClusteredEDFscheduler where each task carries aCpuSetdescribing the set of cores it may run on. Pinning to a single core is now the special case of a one-bit
CpuSet.Key changes:
awkernel_lib: adds a const-friendlyCpuSettype ([u64; NUM_MAX_CPU / 64], i.e. 512 CPUs) withconst fnconstructors (empty,from_bits,insert,contains,is_empty,masked_workers,all_workers) so it can be embedded in the const-evaluatedPRIORITY_LIST.[Mutex<EDFData>; NUM_MAX_CPU]BinaryHeaparray with a single affinity-aware priority queue from theaffinity_btree_queuecrate.get_nextpops the earliest-deadline task runnable on the calling CPU viapop_for_cpu. Priority is(absolute_deadline, wake_time), matching the previous EDF ordering, with FIFO tie-breaking on fully-equal keys.SchedulerType::PartitionedEDF(u64, u16)→ClusteredEDF(u64, CpuSet);Task.partitioned_core: Option<u16>→Task.cpu_set: Option<CpuSet>.spawnmasks out CPU 0 (primary core) and out-of-range bits, and falls back to all worker cores (1..num_cpu()) with a warning when the resulting set is empty.invoke_preemptionnow selects the core running the lowest-priority task among the set, and skips preemption entirely (enqueue only) when any core in the set is idle.PartitionedTask→ClusteredTask(now holding aCpuSetand updating the per-CPU counter for every core in the set),NUM_PARTITIONED_TASKS_IN_QUEUE→NUM_CLUSTERED_TASKS_IN_QUEUE,get_num_partitioned_schedulers→get_num_clustered_schedulers, test cratetest_partitioned_edf→test_clustered_edf.wake_workers—break→continueso higher-numbered CPUs with queued clustered tasks are no longer skipped when the global task count reaches zero.Related links
How was this PR tested?
cargo check_std,cargo check_x86(no_std target),cargo clippy_std— all pass with no warnings.cargo test_awkernel_async_lib— 42 passed.make x86_64 RELEASE=1— builds successfully.-smp 4) with thetest_clustered_edffeature:CpuSettasks ran only on their assigned core (cores 1–3) — all[OK].CpuSet {1, 2}only ever ran on cores 1 or 2 — all[OK].light(earlier deadline) correctly preemptedheavyon the same core.Notes for reviewers
SchedulerTypegrows from 16 bytes to ~72 bytes (stillCopy); it is only passed by value on thespawnpath.get_next_task(false)path (RR preemption tick, primary CPU only) can never dequeue a clustered task because
spawnalways removes CPU 0 from every set — same invariant as before. This is noted in a comment inclustered_edf::get_next.Mutexis only ever acquired whileGLOBAL_WAKE_GET_MUTEXis held (both inwake_taskand via the
get_next_taskwrapper), so no new lock cycle is introduced.invoke_preemptionstill runs before the queue lock is taken.
num_cpu()on first use (AffinityBTreeQueue::newis notconst), following the existing pattern in
prioritized_rr.rs.set_num_cpuruns before the firstspawn, sonum_cp u()is final by then.wake_workersbreak→continuefix addresses a latent lost-wakeup that becomes more reachable withclusters; it is included here rather than split out because the counter semantics changed in the same area.
mdbook/src/internal/scheduler.mdis updated to describe ClusteredEDF and the affinity-aware B-tree queue.