Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
equation314 committed Jul 10, 2024
0 parents commit c8d25d9
Show file tree
Hide file tree
Showing 8 changed files with 630 additions and 0 deletions.
55 changes: 55 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: CI

on: [push, pull_request]

jobs:
ci:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
rust-toolchain: [nightly]
targets: [x86_64-unknown-linux-gnu, x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none-softfloat]
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
with:
toolchain: ${{ matrix.rust-toolchain }}
components: rust-src, clippy, rustfmt
targets: ${{ matrix.targets }}
- name: Check rust version
run: rustc --version --verbose
- name: Check code format
run: cargo fmt --all -- --check
- name: Clippy
run: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default
- name: Build
run: cargo build --target ${{ matrix.targets }} --all-features
- name: Unit test
if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }}
run: cargo test --target ${{ matrix.targets }} -- --nocapture

doc:
runs-on: ubuntu-latest
strategy:
fail-fast: false
permissions:
contents: write
env:
default-branch: ${{ format('refs/heads/{0}', github.event.repository.default_branch) }}
RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- name: Build docs
continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }}
run: |
cargo doc --no-deps --all-features
printf '<meta http-equiv="refresh" content="0;url=%s/index.html">' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html
- name: Deploy to Github Pages
if: ${{ github.ref == env.default-branch }}
uses: JamesIves/github-pages-deploy-action@v4
with:
single-commit: true
branch: gh-pages
folder: target/doc
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/target
/.vscode
.DS_Store
Cargo.lock
14 changes: 14 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "scheduler"
version = "0.1.0"
edition = "2021"
authors = ["Yuekai Jia <[email protected]>"]
description = "Various scheduler algorithms in a unified interface"
license = "GPL-3.0-or-later OR Apache-2.0 OR MulanPSL-2.0"
homepage = "https://github.com/arceos-org/arceos"
repository = "https://github.com/arceos-org/scheduler"
documentation = "https://arceos-org.github.io/scheduler"

[dependencies]
linked_list = { git = "https://github.com/arceos-org/linked_list.git", tag = "v0.1.0" }

190 changes: 190 additions & 0 deletions src/cfs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
use alloc::{collections::BTreeMap, sync::Arc};
use core::ops::Deref;
use core::sync::atomic::{AtomicIsize, Ordering};

use crate::BaseScheduler;

/// task for CFS
pub struct CFSTask<T> {
inner: T,
init_vruntime: AtomicIsize,
delta: AtomicIsize,
nice: AtomicIsize,
id: AtomicIsize,
}

// https://elixir.bootlin.com/linux/latest/source/include/linux/sched/prio.h

const NICE_RANGE_POS: usize = 19; // MAX_NICE in Linux
const NICE_RANGE_NEG: usize = 20; // -MIN_NICE in Linux, the range of nice is [MIN_NICE, MAX_NICE]

// https://elixir.bootlin.com/linux/latest/source/kernel/sched/core.c

const NICE2WEIGHT_POS: [isize; NICE_RANGE_POS + 1] = [
1024, 820, 655, 526, 423, 335, 272, 215, 172, 137, 110, 87, 70, 56, 45, 36, 29, 23, 18, 15,
];
const NICE2WEIGHT_NEG: [isize; NICE_RANGE_NEG + 1] = [
1024, 1277, 1586, 1991, 2501, 3121, 3906, 4904, 6100, 7620, 9548, 11916, 14949, 18705, 23254,
29154, 36291, 46273, 56483, 71755, 88761,
];

impl<T> CFSTask<T> {
/// new with default values
pub const fn new(inner: T) -> Self {
Self {
inner,
init_vruntime: AtomicIsize::new(0_isize),
delta: AtomicIsize::new(0_isize),
nice: AtomicIsize::new(0_isize),
id: AtomicIsize::new(0_isize),
}
}

fn get_weight(&self) -> isize {
let nice = self.nice.load(Ordering::Acquire);
if nice >= 0 {
NICE2WEIGHT_POS[nice as usize]
} else {
NICE2WEIGHT_NEG[(-nice) as usize]
}
}

fn get_id(&self) -> isize {
self.id.load(Ordering::Acquire)
}

fn get_vruntime(&self) -> isize {
if self.nice.load(Ordering::Acquire) == 0 {
self.init_vruntime.load(Ordering::Acquire) + self.delta.load(Ordering::Acquire)
} else {
self.init_vruntime.load(Ordering::Acquire)
+ self.delta.load(Ordering::Acquire) * 1024 / self.get_weight()
}
}

fn set_vruntime(&self, v: isize) {
self.init_vruntime.store(v, Ordering::Release);
}

// Simple Implementation: no change in vruntime.
// Only modifying priority of current process is supported currently.
fn set_priority(&self, nice: isize) {
let current_init_vruntime = self.get_vruntime();
self.init_vruntime
.store(current_init_vruntime, Ordering::Release);
self.delta.store(0, Ordering::Release);
self.nice.store(nice, Ordering::Release);
}

fn set_id(&self, id: isize) {
self.id.store(id, Ordering::Release);
}

fn task_tick(&self) {
self.delta.fetch_add(1, Ordering::Release);
}

/// Returns a reference to the inner task struct.
pub const fn inner(&self) -> &T {
&self.inner
}
}

impl<T> Deref for CFSTask<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}

/// A simple [Completely Fair Scheduler][1] (CFS).
///
/// [1]: https://en.wikipedia.org/wiki/Completely_Fair_Scheduler
pub struct CFScheduler<T> {
ready_queue: BTreeMap<(isize, isize), Arc<CFSTask<T>>>, // (vruntime, taskid)
min_vruntime: Option<AtomicIsize>,
id_pool: AtomicIsize,
}

impl<T> CFScheduler<T> {
/// Creates a new empty [`CFScheduler`].
pub const fn new() -> Self {
Self {
ready_queue: BTreeMap::new(),
min_vruntime: None,
id_pool: AtomicIsize::new(0_isize),
}
}
/// get the name of scheduler
pub fn scheduler_name() -> &'static str {
"Completely Fair"
}
}

impl<T> BaseScheduler for CFScheduler<T> {
type SchedItem = Arc<CFSTask<T>>;

fn init(&mut self) {}

fn add_task(&mut self, task: Self::SchedItem) {
if self.min_vruntime.is_none() {
self.min_vruntime = Some(AtomicIsize::new(0_isize));
}
let vruntime = self.min_vruntime.as_mut().unwrap().load(Ordering::Acquire);
let taskid = self.id_pool.fetch_add(1, Ordering::Release);
task.set_vruntime(vruntime);
task.set_id(taskid);
self.ready_queue.insert((vruntime, taskid), task);
if let Some(((min_vruntime, _), _)) = self.ready_queue.first_key_value() {
self.min_vruntime = Some(AtomicIsize::new(*min_vruntime));
} else {
self.min_vruntime = None;
}
}

fn remove_task(&mut self, task: &Self::SchedItem) -> Option<Self::SchedItem> {
if let Some((_, tmp)) = self
.ready_queue
.remove_entry(&(task.clone().get_vruntime(), task.clone().get_id()))
{
if let Some(((min_vruntime, _), _)) = self.ready_queue.first_key_value() {
self.min_vruntime = Some(AtomicIsize::new(*min_vruntime));
} else {
self.min_vruntime = None;
}
Some(tmp)
} else {
None
}
}

fn pick_next_task(&mut self) -> Option<Self::SchedItem> {
if let Some((_, v)) = self.ready_queue.pop_first() {
Some(v)
} else {
None
}
}

fn put_prev_task(&mut self, prev: Self::SchedItem, _preempt: bool) {
let taskid = self.id_pool.fetch_add(1, Ordering::Release);
prev.set_id(taskid);
self.ready_queue
.insert((prev.clone().get_vruntime(), taskid), prev);
}

fn task_tick(&mut self, current: &Self::SchedItem) -> bool {
current.task_tick();
self.min_vruntime.is_none()
|| current.get_vruntime() > self.min_vruntime.as_mut().unwrap().load(Ordering::Acquire)
}

fn set_priority(&mut self, task: &Self::SchedItem, prio: isize) -> bool {
if (-20..=19).contains(&prio) {
task.set_priority(prio);
true
} else {
false
}
}
}
102 changes: 102 additions & 0 deletions src/fifo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use alloc::sync::Arc;
use core::ops::Deref;

use linked_list::{Adapter, Links, List};

use crate::BaseScheduler;

/// A task wrapper for the [`FifoScheduler`].
///
/// It add extra states to use in [`linked_list::List`].
pub struct FifoTask<T> {
inner: T,
links: Links<Self>,
}

unsafe impl<T> Adapter for FifoTask<T> {
type EntryType = Self;

#[inline]
fn to_links(t: &Self) -> &Links<Self> {
&t.links
}
}

impl<T> FifoTask<T> {
/// Creates a new [`FifoTask`] from the inner task struct.
pub const fn new(inner: T) -> Self {
Self {
inner,
links: Links::new(),
}
}

/// Returns a reference to the inner task struct.
pub const fn inner(&self) -> &T {
&self.inner
}
}

impl<T> Deref for FifoTask<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
&self.inner
}
}

/// A simple FIFO (First-In-First-Out) cooperative scheduler.
///
/// When a task is added to the scheduler, it's placed at the end of the ready
/// queue. When picking the next task to run, the head of the ready queue is
/// taken.
///
/// As it's a cooperative scheduler, it does nothing when the timer tick occurs.
///
/// It internally uses a linked list as the ready queue.
pub struct FifoScheduler<T> {
ready_queue: List<Arc<FifoTask<T>>>,
}

impl<T> FifoScheduler<T> {
/// Creates a new empty [`FifoScheduler`].
pub const fn new() -> Self {
Self {
ready_queue: List::new(),
}
}
/// get the name of scheduler
pub fn scheduler_name() -> &'static str {
"FIFO"
}
}

impl<T> BaseScheduler for FifoScheduler<T> {
type SchedItem = Arc<FifoTask<T>>;

fn init(&mut self) {}

fn add_task(&mut self, task: Self::SchedItem) {
self.ready_queue.push_back(task);
}

fn remove_task(&mut self, task: &Self::SchedItem) -> Option<Self::SchedItem> {
unsafe { self.ready_queue.remove(task) }
}

fn pick_next_task(&mut self) -> Option<Self::SchedItem> {
self.ready_queue.pop_front()
}

fn put_prev_task(&mut self, prev: Self::SchedItem, _preempt: bool) {
self.ready_queue.push_back(prev);
}

fn task_tick(&mut self, _current: &Self::SchedItem) -> bool {
false // no reschedule
}

fn set_priority(&mut self, _task: &Self::SchedItem, _prio: isize) -> bool {
false
}
}
Loading

0 comments on commit c8d25d9

Please sign in to comment.