Skip to content

Commit c8d25d9

Browse files
committed
Initial commit
0 parents  commit c8d25d9

8 files changed

Lines changed: 630 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: CI
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
ci:
7+
runs-on: ubuntu-latest
8+
strategy:
9+
fail-fast: false
10+
matrix:
11+
rust-toolchain: [nightly]
12+
targets: [x86_64-unknown-linux-gnu, x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none-softfloat]
13+
steps:
14+
- uses: actions/checkout@v4
15+
- uses: dtolnay/rust-toolchain@nightly
16+
with:
17+
toolchain: ${{ matrix.rust-toolchain }}
18+
components: rust-src, clippy, rustfmt
19+
targets: ${{ matrix.targets }}
20+
- name: Check rust version
21+
run: rustc --version --verbose
22+
- name: Check code format
23+
run: cargo fmt --all -- --check
24+
- name: Clippy
25+
run: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default
26+
- name: Build
27+
run: cargo build --target ${{ matrix.targets }} --all-features
28+
- name: Unit test
29+
if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }}
30+
run: cargo test --target ${{ matrix.targets }} -- --nocapture
31+
32+
doc:
33+
runs-on: ubuntu-latest
34+
strategy:
35+
fail-fast: false
36+
permissions:
37+
contents: write
38+
env:
39+
default-branch: ${{ format('refs/heads/{0}', github.event.repository.default_branch) }}
40+
RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs
41+
steps:
42+
- uses: actions/checkout@v4
43+
- uses: dtolnay/rust-toolchain@nightly
44+
- name: Build docs
45+
continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }}
46+
run: |
47+
cargo doc --no-deps --all-features
48+
printf '<meta http-equiv="refresh" content="0;url=%s/index.html">' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html
49+
- name: Deploy to Github Pages
50+
if: ${{ github.ref == env.default-branch }}
51+
uses: JamesIves/github-pages-deploy-action@v4
52+
with:
53+
single-commit: true
54+
branch: gh-pages
55+
folder: target/doc

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/target
2+
/.vscode
3+
.DS_Store
4+
Cargo.lock

Cargo.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "scheduler"
3+
version = "0.1.0"
4+
edition = "2021"
5+
authors = ["Yuekai Jia <equation618@gmail.com>"]
6+
description = "Various scheduler algorithms in a unified interface"
7+
license = "GPL-3.0-or-later OR Apache-2.0 OR MulanPSL-2.0"
8+
homepage = "https://github.com/arceos-org/arceos"
9+
repository = "https://github.com/arceos-org/scheduler"
10+
documentation = "https://arceos-org.github.io/scheduler"
11+
12+
[dependencies]
13+
linked_list = { git = "https://github.com/arceos-org/linked_list.git", tag = "v0.1.0" }
14+

src/cfs.rs

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
use alloc::{collections::BTreeMap, sync::Arc};
2+
use core::ops::Deref;
3+
use core::sync::atomic::{AtomicIsize, Ordering};
4+
5+
use crate::BaseScheduler;
6+
7+
/// task for CFS
8+
pub struct CFSTask<T> {
9+
inner: T,
10+
init_vruntime: AtomicIsize,
11+
delta: AtomicIsize,
12+
nice: AtomicIsize,
13+
id: AtomicIsize,
14+
}
15+
16+
// https://elixir.bootlin.com/linux/latest/source/include/linux/sched/prio.h
17+
18+
const NICE_RANGE_POS: usize = 19; // MAX_NICE in Linux
19+
const NICE_RANGE_NEG: usize = 20; // -MIN_NICE in Linux, the range of nice is [MIN_NICE, MAX_NICE]
20+
21+
// https://elixir.bootlin.com/linux/latest/source/kernel/sched/core.c
22+
23+
const NICE2WEIGHT_POS: [isize; NICE_RANGE_POS + 1] = [
24+
1024, 820, 655, 526, 423, 335, 272, 215, 172, 137, 110, 87, 70, 56, 45, 36, 29, 23, 18, 15,
25+
];
26+
const NICE2WEIGHT_NEG: [isize; NICE_RANGE_NEG + 1] = [
27+
1024, 1277, 1586, 1991, 2501, 3121, 3906, 4904, 6100, 7620, 9548, 11916, 14949, 18705, 23254,
28+
29154, 36291, 46273, 56483, 71755, 88761,
29+
];
30+
31+
impl<T> CFSTask<T> {
32+
/// new with default values
33+
pub const fn new(inner: T) -> Self {
34+
Self {
35+
inner,
36+
init_vruntime: AtomicIsize::new(0_isize),
37+
delta: AtomicIsize::new(0_isize),
38+
nice: AtomicIsize::new(0_isize),
39+
id: AtomicIsize::new(0_isize),
40+
}
41+
}
42+
43+
fn get_weight(&self) -> isize {
44+
let nice = self.nice.load(Ordering::Acquire);
45+
if nice >= 0 {
46+
NICE2WEIGHT_POS[nice as usize]
47+
} else {
48+
NICE2WEIGHT_NEG[(-nice) as usize]
49+
}
50+
}
51+
52+
fn get_id(&self) -> isize {
53+
self.id.load(Ordering::Acquire)
54+
}
55+
56+
fn get_vruntime(&self) -> isize {
57+
if self.nice.load(Ordering::Acquire) == 0 {
58+
self.init_vruntime.load(Ordering::Acquire) + self.delta.load(Ordering::Acquire)
59+
} else {
60+
self.init_vruntime.load(Ordering::Acquire)
61+
+ self.delta.load(Ordering::Acquire) * 1024 / self.get_weight()
62+
}
63+
}
64+
65+
fn set_vruntime(&self, v: isize) {
66+
self.init_vruntime.store(v, Ordering::Release);
67+
}
68+
69+
// Simple Implementation: no change in vruntime.
70+
// Only modifying priority of current process is supported currently.
71+
fn set_priority(&self, nice: isize) {
72+
let current_init_vruntime = self.get_vruntime();
73+
self.init_vruntime
74+
.store(current_init_vruntime, Ordering::Release);
75+
self.delta.store(0, Ordering::Release);
76+
self.nice.store(nice, Ordering::Release);
77+
}
78+
79+
fn set_id(&self, id: isize) {
80+
self.id.store(id, Ordering::Release);
81+
}
82+
83+
fn task_tick(&self) {
84+
self.delta.fetch_add(1, Ordering::Release);
85+
}
86+
87+
/// Returns a reference to the inner task struct.
88+
pub const fn inner(&self) -> &T {
89+
&self.inner
90+
}
91+
}
92+
93+
impl<T> Deref for CFSTask<T> {
94+
type Target = T;
95+
fn deref(&self) -> &Self::Target {
96+
&self.inner
97+
}
98+
}
99+
100+
/// A simple [Completely Fair Scheduler][1] (CFS).
101+
///
102+
/// [1]: https://en.wikipedia.org/wiki/Completely_Fair_Scheduler
103+
pub struct CFScheduler<T> {
104+
ready_queue: BTreeMap<(isize, isize), Arc<CFSTask<T>>>, // (vruntime, taskid)
105+
min_vruntime: Option<AtomicIsize>,
106+
id_pool: AtomicIsize,
107+
}
108+
109+
impl<T> CFScheduler<T> {
110+
/// Creates a new empty [`CFScheduler`].
111+
pub const fn new() -> Self {
112+
Self {
113+
ready_queue: BTreeMap::new(),
114+
min_vruntime: None,
115+
id_pool: AtomicIsize::new(0_isize),
116+
}
117+
}
118+
/// get the name of scheduler
119+
pub fn scheduler_name() -> &'static str {
120+
"Completely Fair"
121+
}
122+
}
123+
124+
impl<T> BaseScheduler for CFScheduler<T> {
125+
type SchedItem = Arc<CFSTask<T>>;
126+
127+
fn init(&mut self) {}
128+
129+
fn add_task(&mut self, task: Self::SchedItem) {
130+
if self.min_vruntime.is_none() {
131+
self.min_vruntime = Some(AtomicIsize::new(0_isize));
132+
}
133+
let vruntime = self.min_vruntime.as_mut().unwrap().load(Ordering::Acquire);
134+
let taskid = self.id_pool.fetch_add(1, Ordering::Release);
135+
task.set_vruntime(vruntime);
136+
task.set_id(taskid);
137+
self.ready_queue.insert((vruntime, taskid), task);
138+
if let Some(((min_vruntime, _), _)) = self.ready_queue.first_key_value() {
139+
self.min_vruntime = Some(AtomicIsize::new(*min_vruntime));
140+
} else {
141+
self.min_vruntime = None;
142+
}
143+
}
144+
145+
fn remove_task(&mut self, task: &Self::SchedItem) -> Option<Self::SchedItem> {
146+
if let Some((_, tmp)) = self
147+
.ready_queue
148+
.remove_entry(&(task.clone().get_vruntime(), task.clone().get_id()))
149+
{
150+
if let Some(((min_vruntime, _), _)) = self.ready_queue.first_key_value() {
151+
self.min_vruntime = Some(AtomicIsize::new(*min_vruntime));
152+
} else {
153+
self.min_vruntime = None;
154+
}
155+
Some(tmp)
156+
} else {
157+
None
158+
}
159+
}
160+
161+
fn pick_next_task(&mut self) -> Option<Self::SchedItem> {
162+
if let Some((_, v)) = self.ready_queue.pop_first() {
163+
Some(v)
164+
} else {
165+
None
166+
}
167+
}
168+
169+
fn put_prev_task(&mut self, prev: Self::SchedItem, _preempt: bool) {
170+
let taskid = self.id_pool.fetch_add(1, Ordering::Release);
171+
prev.set_id(taskid);
172+
self.ready_queue
173+
.insert((prev.clone().get_vruntime(), taskid), prev);
174+
}
175+
176+
fn task_tick(&mut self, current: &Self::SchedItem) -> bool {
177+
current.task_tick();
178+
self.min_vruntime.is_none()
179+
|| current.get_vruntime() > self.min_vruntime.as_mut().unwrap().load(Ordering::Acquire)
180+
}
181+
182+
fn set_priority(&mut self, task: &Self::SchedItem, prio: isize) -> bool {
183+
if (-20..=19).contains(&prio) {
184+
task.set_priority(prio);
185+
true
186+
} else {
187+
false
188+
}
189+
}
190+
}

src/fifo.rs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
use alloc::sync::Arc;
2+
use core::ops::Deref;
3+
4+
use linked_list::{Adapter, Links, List};
5+
6+
use crate::BaseScheduler;
7+
8+
/// A task wrapper for the [`FifoScheduler`].
9+
///
10+
/// It add extra states to use in [`linked_list::List`].
11+
pub struct FifoTask<T> {
12+
inner: T,
13+
links: Links<Self>,
14+
}
15+
16+
unsafe impl<T> Adapter for FifoTask<T> {
17+
type EntryType = Self;
18+
19+
#[inline]
20+
fn to_links(t: &Self) -> &Links<Self> {
21+
&t.links
22+
}
23+
}
24+
25+
impl<T> FifoTask<T> {
26+
/// Creates a new [`FifoTask`] from the inner task struct.
27+
pub const fn new(inner: T) -> Self {
28+
Self {
29+
inner,
30+
links: Links::new(),
31+
}
32+
}
33+
34+
/// Returns a reference to the inner task struct.
35+
pub const fn inner(&self) -> &T {
36+
&self.inner
37+
}
38+
}
39+
40+
impl<T> Deref for FifoTask<T> {
41+
type Target = T;
42+
#[inline]
43+
fn deref(&self) -> &Self::Target {
44+
&self.inner
45+
}
46+
}
47+
48+
/// A simple FIFO (First-In-First-Out) cooperative scheduler.
49+
///
50+
/// When a task is added to the scheduler, it's placed at the end of the ready
51+
/// queue. When picking the next task to run, the head of the ready queue is
52+
/// taken.
53+
///
54+
/// As it's a cooperative scheduler, it does nothing when the timer tick occurs.
55+
///
56+
/// It internally uses a linked list as the ready queue.
57+
pub struct FifoScheduler<T> {
58+
ready_queue: List<Arc<FifoTask<T>>>,
59+
}
60+
61+
impl<T> FifoScheduler<T> {
62+
/// Creates a new empty [`FifoScheduler`].
63+
pub const fn new() -> Self {
64+
Self {
65+
ready_queue: List::new(),
66+
}
67+
}
68+
/// get the name of scheduler
69+
pub fn scheduler_name() -> &'static str {
70+
"FIFO"
71+
}
72+
}
73+
74+
impl<T> BaseScheduler for FifoScheduler<T> {
75+
type SchedItem = Arc<FifoTask<T>>;
76+
77+
fn init(&mut self) {}
78+
79+
fn add_task(&mut self, task: Self::SchedItem) {
80+
self.ready_queue.push_back(task);
81+
}
82+
83+
fn remove_task(&mut self, task: &Self::SchedItem) -> Option<Self::SchedItem> {
84+
unsafe { self.ready_queue.remove(task) }
85+
}
86+
87+
fn pick_next_task(&mut self) -> Option<Self::SchedItem> {
88+
self.ready_queue.pop_front()
89+
}
90+
91+
fn put_prev_task(&mut self, prev: Self::SchedItem, _preempt: bool) {
92+
self.ready_queue.push_back(prev);
93+
}
94+
95+
fn task_tick(&mut self, _current: &Self::SchedItem) -> bool {
96+
false // no reschedule
97+
}
98+
99+
fn set_priority(&mut self, _task: &Self::SchedItem, _prio: isize) -> bool {
100+
false
101+
}
102+
}

0 commit comments

Comments
 (0)