Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c26607b
feat(sched): add CPU core affinity via ClusteredEDF scheduler
ytakano Jul 7, 2026
8c3dd66
update documents
ytakano Jul 7, 2026
6428162
add `any` method to `CpuSet`
ytakano Jul 7, 2026
84c90c9
Potential fix for pull request finding
ytakano Jul 7, 2026
da86164
consider if `task` is `Some`
ytakano Jul 7, 2026
eb784de
fix the comment
ytakano Jul 7, 2026
4617978
perf(sched): make clustered-task bookkeeping O(1) per operation
ytakano Jul 21, 2026
f30359d
Merge branch 'main' into core_affinity
ytakano Jul 21, 2026
640e4da
docs(sched): note the preemption latency window in invoke_preemption
ytakano Jul 21, 2026
e6b070f
test(cpu): add unit tests for CpuSet worker-mask logic
ytakano Jul 22, 2026
f61c054
test(sched): cover clustered CPU-set fallback and document it
ytakano Jul 22, 2026
7443459
feat(cpu): enforce num_cpu() >= 2 at boot
ytakano Jul 22, 2026
eab6c54
perf(sched): take &SchedulerType in get_scheduler/get_priority
ytakano Jul 22, 2026
2b1efe0
refactor(sched): mark ClusteredEDF wake-time set check as unreachable
ytakano Jul 22, 2026
5301ca2
build(deps): vendor affinity_btree_queue as a workspace member
ytakano Jul 22, 2026
6b5e25d
fix bool.toml
ytakano Jul 22, 2026
ae9ebcc
Merge branch 'main' into core_affinity
ytakano Jul 22, 2026
ce9dada
Merge branch 'core_affinity' of github.com:ytakano/awkernel into core…
ytakano Jul 22, 2026
b248661
fix(docs): regenerate docs
ytakano Jul 22, 2026
bda7881
remove LICENSE.md
ytakano Jul 22, 2026
9e283d5
add mdbook/src/LICENSE.md
ytakano Jul 22, 2026
0e2e9f5
Merge branch 'main' into core_affinity
ytakano Jul 23, 2026
292fe32
fix: address review comments on ClusteredEDF PR
ytakano Jul 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ test_awkernel_lib = "test --package awkernel_lib --no-default-features --feature
test_awkernel_async_lib = "test --package awkernel_async_lib --no-default-features --features std"
test_awkernel_drivers = "test --package awkernel_drivers --no-default-features --features std"
test_smoltcp = "test --package smoltcp --no-default-features --features std --features awkernel"
test_affinity_btree_queue = "test --package affinity_btree_queue"
test_rd_gen_to_dags = "test --package rd_gen_to_dags --no-default-features --features std --features milliseconds"

clippy_x86 = "clippy --package awkernel --no-default-features --features x86 --target targets/x86_64-kernel.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

[workspace]
members = [
"affinity_btree_queue",
"kernel",
"userland",
"x86bootdisk",
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ test: FORCE
cargo test_awkernel_async_lib -- --nocapture
cargo test_awkernel_drivers
cargo test_smoltcp
cargo test_affinity_btree_queue
cargo test_rd_gen_to_dags

# Format
Expand Down
12 changes: 12 additions & 0 deletions affinity_btree_queue/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "affinity_btree_queue"
version = "0.1.1"
edition = "2024"
license = "MIT OR Apache-2.0"
description = "An affinity-aware priority queue backed by an augmented classical (CLRS-style) B-tree."
readme = "README.md"
keywords = ["no-std", "priority-queue"]
categories = ["data-structures"]
repository = "https://github.com/ytakano/affinity_btree_queue"

[dependencies]
159 changes: 159 additions & 0 deletions affinity_btree_queue/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# Affinity BTree Queue

An affinity-aware priority queue backed by an augmented classical (CLRS-style) B-tree, written in Rust.

Each entry carries `(priority, affinity: CpuMask, value)`.
`pop_for_cpu(cpu)` returns the highest-priority entry whose affinity includes that CPU in O(log N),
making it suitable for task schedulers where tasks are pinned to specific CPUs.

## Features

- **Affinity-aware scheduling** — every node stores the OR of all affinities in its subtree;
`pop_for_cpu` prunes ineligible subtrees in a single traversal
- **`no_std` + `alloc`** — works in embedded and OS-kernel environments
- **O(log N)** push / pop / remove via an arena-based augmented B-tree
- **Generic priority** — any `P: Ord`; smaller value = higher priority;
use `core::cmp::Reverse` for max-first order
- **Configurable CPU count** — `CpuMask<N>` supports up to `N × 64` CPUs
(default `N = 1` → 64 CPUs, `N = 2` → 128 CPUs, …)
- **No unsafe code** — `#![forbid(unsafe_code)]`

## Usage

Add the crate to `Cargo.toml`:

```toml
[dependencies]
affinity_btree_queue = { path = "..." }
```

### Basic example

```rust
use affinity_btree_queue::{AffinityBTreeQueue, CpuMask};

// Create a queue for a 4-CPU system.
// Smaller priority value = higher priority.
let mut q: AffinityBTreeQueue<u32, &str> = AffinityBTreeQueue::new(4);

// Build CPU affinity masks.
let mut cpu0 = CpuMask::empty();
cpu0.insert(0);

let mut cpu12 = CpuMask::empty();
cpu12.insert(1);
cpu12.insert(2);

let mut all = CpuMask::empty();
for c in 0..4 { all.insert(c); }

// push returns a PriorityKey handle that can be passed to remove() later.
q.push(10, all, "low priority, any CPU").unwrap();
q.push(5, cpu12, "medium, CPUs 1-2 only").unwrap();
q.push(1, cpu0, "high priority, CPU 0 only").unwrap();

// pop_for_cpu returns the highest-priority entry runnable on the given CPU.
let (_, _, v) = q.pop_for_cpu(0).unwrap();
assert_eq!(v, "high priority, CPU 0 only"); // priority=1

// The cpu12 entry (priority=5) is not runnable on CPU 0, so priority=10 is next.
let (_, _, v) = q.pop_for_cpu(0).unwrap();
assert_eq!(v, "low priority, any CPU"); // priority=10

// CPU 1 can run the cpu12 entry (priority=5).
let (_, _, v) = q.pop_for_cpu(1).unwrap();
assert_eq!(v, "medium, CPUs 1-2 only"); // priority=5
```

### Max-first ordering with `Reverse`

```rust
use affinity_btree_queue::{AffinityBTreeQueue, CpuMask};
use core::cmp::Reverse;

let mut q: AffinityBTreeQueue<Reverse<u32>, u32> = AffinityBTreeQueue::new(1);
let mut m = CpuMask::empty();
m.insert(0);
q.push(Reverse(1), m, 1).unwrap();
q.push(Reverse(100), m, 100).unwrap();

// Reverse(100) < Reverse(1), so the numerically larger value dequeues first.
let (_, _, v) = q.pop_for_cpu(0).unwrap();
assert_eq!(v, 100);
```

## API overview

### `AffinityBTreeQueue<P, V, const T = 16, const N = 1>`

| Method | Description |
|--------|-------------|
| `new(num_cpus)` | Create an empty queue for CPUs `0..num_cpus` |
| `push(priority, affinity, value)` | Insert an entry; returns a `PriorityKey` handle |
| `pop_for_cpu(cpu)` | Remove and return the highest-priority entry runnable on `cpu` |
| `peek_key_for_cpu(cpu)` | Return the key of the best entry for `cpu` without removing it |
| `remove(&key)` | Remove an entry by its `PriorityKey` |
| `len()` / `is_empty()` | Entry count |
| `num_cpus()` | Configured CPU count |

Type parameters: `P: Ord` (priority), `V` (value), `T` (B-tree minimum degree, default 16),
`N` (words in `CpuMask`, default 1).

### `CpuMask<const N = 1>`

| Method | Description |
|--------|-------------|
| `empty()` | All-zero mask |
| `insert(cpu)` | Set the bit for `cpu` |
| `contains(cpu)` | Test whether `cpu` is set |
| `union(other)` | Bitwise OR of two masks |
| `from_bits_truncate(bits)` | Construct from a raw `u64` (word 0) |

### `PushError`

| Variant | Meaning |
|---------|---------|
| `EmptyAffinity` | `affinity` has no bits set |
| `InvalidCpu` | `affinity` has a bit set for a CPU ≥ `num_cpus` |
| `SequenceOverflow` | Internal sequence counter exhausted (2⁶⁴ pushes) |

## Building and testing

```bash
cargo build
cargo test # unit tests + randomized property tests + doctests
```

Tests compare every operation against a simple `Vec`-based reference model and verify
all B-tree structural invariants (key order, subtree affinity summaries, leaf depth
uniformity, entry count) after every mutation.

## Implementation notes

- The tree is an **arena-based augmented B-tree** (`Vec<Option<Node>>` + free list).
Node IDs are stable indices; the arena is never compacted.
- Every node stores **`subtree_affinity`** = OR of all entry affinities in its subtree.
`pop_for_cpu` uses this summary to skip entire subtrees with no eligible entry.
- Ordering is by **`(priority, seq)`** — the monotonically increasing `seq` counter
makes every key unique, so no two entries ever compare equal.
- Priority / affinity updates are **remove + re-insert**; no in-place mutation of keys.
- Deletion is **top-down**: before descending into a child, the algorithm ensures it has
≥ T entries (borrowing from a sibling or merging), so underflow never propagates upward.

## License

This project is licensed under either of

- Apache License, Version 2.0
- MIT license

at your option.

## Test Coverage

Line-by-line coverage data can be generated locally with `cargo llvm-cov`:

```bash
cargo llvm-cov --workspace --all-features --text --output-path coverage.txt
cargo llvm-cov --workspace --all-features --lcov --output-path lcov.info
```
Loading
Loading