Skip to content

feat(sched): add CPU core affinity via ClusteredEDF scheduler#701

Open
ytakano wants to merge 6 commits into
tier4:mainfrom
ytakano:core_affinity
Open

feat(sched): add CPU core affinity via ClusteredEDF scheduler#701
ytakano wants to merge 6 commits into
tier4:mainfrom
ytakano:core_affinity

Conversation

@ytakano

@ytakano ytakano commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Description

Generalizes the PartitionedEDF scheduler — which pinned each task to a single core — into a ClusteredEDF scheduler where each task carries a CpuSet describing the set of cores it may run on. Pinning to a sing
le core is now the special case of a one-bit CpuSet.

Key changes:

  • awkernel_lib: adds a const-friendly CpuSet type ([u64; NUM_MAX_CPU / 64], i.e. 512 CPUs) with const fn constructors (empty, from_bits, insert, contains, is_empty, masked_workers, all_workers) so it can be embedded in the const-evaluated PRIORITY_LIST.
  • Run queue: replaces the per-core [Mutex<EDFData>; NUM_MAX_CPU] BinaryHeap array with a single affinity-aware priority queue from the affinity_btree_queue crate. get_next pops the earliest-deadline task runnable on the calling CPU via pop_for_cpu. Priority is (absolute_deadline, wake_time), matching the previous EDF ordering, with FIFO tie-breaking on fully-equal keys.
  • API: SchedulerType::PartitionedEDF(u64, u16)ClusteredEDF(u64, CpuSet); Task.partitioned_core: Option<u16>Task.cpu_set: Option<CpuSet>. spawn masks 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.
  • Preemption: invoke_preemption now 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.
  • Renames: PartitionedTaskClusteredTask (now holding a CpuSet and updating the per-CPU counter for every core in the set), NUM_PARTITIONED_TASKS_IN_QUEUENUM_CLUSTERED_TASKS_IN_QUEUE, get_num_partitioned_schedulersget_num_clustered_schedulers, test crate test_partitioned_edftest_clustered_edf.
  • Bug fix: fixes a pre-existing lost-wakeup bug in wake_workersbreakcontinue so 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.
  • Booted in QEMU (-smp 4) with the test_clustered_edf feature:
    • core pinning: single-core CpuSet tasks ran only on their assigned core (cores 1–3) — all [OK].
    • cluster affinity: a task with CpuSet {1, 2} only ever ran on cores 1 or 2 — all [OK].
    • EDF preemption: light (earlier deadline) correctly preempted heavy on the same core.
    • multi-core: tasks on cores 1 and 2 ran in parallel without interference.

Notes for reviewers

  • SchedulerType grows from 16 bytes to ~72 bytes (still Copy); it is only passed by value on the spawn path.
  • The get_next_task(false) path (RR preemption tick, primary CPU only) can never dequeue a clustered task b
    ecause spawn always removes CPU 0 from every set — same invariant as before. This is noted in a comment in
    clustered_edf::get_next.
  • The single queue Mutex is only ever acquired while GLOBAL_WAKE_GET_MUTEX is held (both in wake_task a
    nd via the get_next_task wrapper), so no new lock cycle is introduced. invoke_preemption still runs befor
    e the queue lock is taken.
  • The queue is lazily initialized with num_cpu() on first use (AffinityBTreeQueue::new is not const), f
    ollowing the existing pattern in prioritized_rr.rs. set_num_cpu runs before the first spawn, so num_cp u() is final by then.
  • The wake_workers breakcontinue fix addresses a latent lost-wakeup that becomes more reachable with
    clusters; it is included here rather than split out because the counter semantics changed in the same area.
  • mdbook/src/internal/scheduler.md is updated to describe ClusteredEDF and the affinity-aware B-tree queue.

ytakano and others added 2 commits July 7, 2026 14:56
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>
@ytakano ytakano changed the title Core affinity feat(sched): add CPU core affinity via ClusteredEDF scheduler Jul 7, 2026
Signed-off-by: Yuuki Takano <ytakanoster@gmail.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 new clustered_edf scheduler implementation.
  • Rename and update the userland/kernel test feature and test crate from test_partitioned_edf to test_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.

Comment thread awkernel_async_lib/src/scheduler.rs Outdated
Comment thread awkernel_lib/src/cpu.rs Outdated
Comment thread awkernel_lib/src/cpu.rs Outdated
Comment thread docs/print.html
ytakano and others added 3 commits July 7, 2026 15:51
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>
@ytakano ytakano marked this pull request as ready for review July 7, 2026 08:40
@ytakano ytakano requested review from atsushi421 and nokosaaan July 7, 2026 08:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants