Skip to content

Commit

Permalink
Merge remote-tracking branch 'm/main' into qcl_dev
Browse files Browse the repository at this point in the history
  • Loading branch information
ZR233 committed Nov 26, 2024
2 parents c76a8a7 + 82d9a05 commit 1575924
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 82 deletions.
17 changes: 17 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
FROM rust:slim

RUN apt-get update \
&& apt-get install -y --no-install-recommends libclang-dev qemu-system wget make \
&& rm -rf /var/lib/apt/lists/*

RUN cargo install cargo-binutils

RUN wget https://musl.cc/aarch64-linux-musl-cross.tgz \
&& wget https://musl.cc/riscv64-linux-musl-cross.tgz \
&& wget https://musl.cc/x86_64-linux-musl-cross.tgz \
&& tar zxf aarch64-linux-musl-cross.tgz \
&& tar zxf riscv64-linux-musl-cross.tgz \
&& tar zxf x86_64-linux-musl-cross.tgz \
&& rm -f *.tgz

ENV PATH="/x86_64-linux-musl-cross/bin:/aarch64-linux-musl-cross/bin:/riscv64-linux-musl-cross/bin:$PATH"
28 changes: 24 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,35 @@ ArceOS was inspired a lot by [Unikraft](https://github.com/unikraft/unikraft).

## Quick Start

### 1. Install Build Dependencies
### Build and Run through Docker

Install [Docker](https://www.docker.com/) in your system.

Then build all dependencies through provided dockerfile:

```bash
docker build -t arceos -f Dockerfile .
```

Create a container and build/run app:
```bash
docker run -it -v $(pwd):/arceos -w /arceos arceos bash

# Now build/run app in the container
make A=examples/helloworld ARCH=aarch64 run
```


### Manually Build and Run
#### 1. Install Build Dependencies

Install [cargo-binutils](https://github.com/rust-embedded/cargo-binutils) to use `rust-objcopy` and `rust-objdump` tools:

```bash
cargo install cargo-binutils
```

#### Dependencies for C apps
##### Dependencies for C apps

Install `libclang-dev`:

Expand All @@ -58,7 +78,7 @@ tar zxf x86_64-linux-musl-cross.tgz
export PATH=`pwd`/x86_64-linux-musl-cross/bin:`pwd`/aarch64-linux-musl-cross/bin:`pwd`/riscv64-linux-musl-cross/bin:$PATH
```

#### Dependencies for running apps
##### Dependencies for running apps

```bash
# for Debian/Ubuntu
Expand All @@ -72,7 +92,7 @@ brew install qemu

Other systems and arch please refer to [Qemu Download](https://www.qemu.org/download/#linux)

### 2. Build & Run
#### 2. Build & Run

```bash
# build app in arceos directory
Expand Down
8 changes: 4 additions & 4 deletions doc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ Modules are **OS-related** components that are tightly bound to the design princ

Most of the modules are optional, depending on the features enabled by the application. A few modules are required by all applications, as shown below:

* [axruntime](modules/axruntime/): Bootstrapping from the bare-metal environment, and initialization.
* [axhal](modules/axhal/): Hardware abstraction layer, provides unified APIs for cross-platform.
* [axconfig](modules/axconfig/): Platform constants and kernel parameters, such as physical memory base, kernel load addresses, stack size, etc.
* [axlog](modules/axlog/): Multi-level formatted logging.
* [axruntime](../modules/axruntime/): Bootstrapping from the bare-metal environment, and initialization.
* [axhal](../modules/axhal/): Hardware abstraction layer, provides unified APIs for cross-platform.
* [axconfig](../modules/axconfig/): Platform constants and kernel parameters, such as physical memory base, kernel load addresses, stack size, etc.
* [axlog](../modules/axlog/): Multi-level formatted logging.

Other optional modules and their corresponding features are as follows:

Expand Down
4 changes: 2 additions & 2 deletions modules/axhal/src/platform/riscv64_qemu_virt/boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ static mut BOOT_PT_SV39: [u64; 512] = [0; 512];

unsafe fn init_boot_page_table() {
// 0x0000_0000..0x4000_0000, VRWX_GAD, 1G block
BOOT_PT_SV39[0] = (0x80000 << 10) | 0xef;
BOOT_PT_SV39[0] = (0x0 << 10) | 0xef;
// 0x8000_0000..0xc000_0000, VRWX_GAD, 1G block
BOOT_PT_SV39[2] = (0x80000 << 10) | 0xef;
// 0xffff_ffc0_0000_0000..0xffff_ffc0_4000_0000, VRWX_GAD, 1G block
BOOT_PT_SV39[0x100] = (0x80000 << 10) | 0xef;
BOOT_PT_SV39[0x100] = (0x0 << 10) | 0xef;
// 0xffff_ffc0_8000_0000..0xffff_ffc0_c000_0000, VRWX_GAD, 1G block
BOOT_PT_SV39[0x102] = (0x80000 << 10) | 0xef;
}
Expand Down
20 changes: 17 additions & 3 deletions modules/axtask/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,26 @@ pub fn set_current_affinity(cpumask: AxCpuMask) -> bool {
if cpumask.is_empty() {
false
} else {
let mut rq = current_run_queue::<NoPreemptIrqSave>();
current().set_cpumask(cpumask);
let curr = current().clone();

curr.set_cpumask(cpumask);
// After setting the affinity, we need to check if current cpu matches
// the affinity. If not, we need to migrate the task to the correct CPU.
#[cfg(feature = "smp")]
if !cpumask.get(axhal::cpu::this_cpu_id()) {
rq.migrate_current();
const MIGRATION_TASK_STACK_SIZE: usize = 4096;
// Spawn a new migration task for migrating.
let migration_task = TaskInner::new(
move || crate::run_queue::migrate_entry(curr),
"migration-task".into(),
MIGRATION_TASK_STACK_SIZE,
)
.into_arc();

// Migrate the current task to the correct CPU using the migration task.
current_run_queue::<NoPreemptIrqSave>().migrate_current(migration_task);

assert!(cpumask.get(axhal::cpu::this_cpu_id()), "Migration failed");
}
true
}
Expand Down
108 changes: 80 additions & 28 deletions modules/axtask/src/run_queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ use alloc::collections::VecDeque;
use alloc::sync::Arc;
use core::mem::MaybeUninit;

use kernel_guard::{BaseGuard, NoOp};
#[cfg(feature = "smp")]
use alloc::sync::Weak;

use kernel_guard::BaseGuard;
use kspin::SpinRaw;
use lazyinit::LazyInit;
use scheduler::BaseScheduler;
Expand All @@ -14,8 +17,12 @@ use crate::wait_queue::WaitQueueGuard;
use crate::{AxCpuMask, AxTaskRef, Scheduler, TaskInner, WaitQueue};

macro_rules! percpu_static {
($($name:ident: $ty:ty = $init:expr),* $(,)?) => {
($(
$(#[$comment:meta])*
$name:ident: $ty:ty = $init:expr
),* $(,)?) => {
$(
$(#[$comment])*
#[percpu::def_percpu]
static $name: $ty = $init;
)*
Expand All @@ -27,6 +34,9 @@ percpu_static! {
EXITED_TASKS: VecDeque<AxTaskRef> = VecDeque::new(),
WAIT_FOR_EXIT: WaitQueue = WaitQueue::new(),
IDLE_TASK: LazyInit<AxTaskRef> = LazyInit::new(),
/// Stores the weak reference to the previous task that is running on this CPU.
#[cfg(feature = "smp")]
PREV_TASK: Weak<crate::AxTask> = Weak::new(),
}

/// An array of references to run queues, one for each CPU, indexed by cpu_id.
Expand Down Expand Up @@ -216,6 +226,9 @@ impl<'a, G: BaseGuard> Drop for CurrentRunQueueRef<'a, G> {

/// Management operations for run queue, including adding tasks, unblocking tasks, etc.
impl<'a, G: BaseGuard> AxRunQueueRef<'a, G> {
/// Adds a task to the scheduler.
///
/// This function is used to add a new task to the scheduler.
pub fn add_task(&mut self, task: AxTaskRef) {
debug!(
"task add: {} on run_queue {}",
Expand All @@ -227,11 +240,16 @@ impl<'a, G: BaseGuard> AxRunQueueRef<'a, G> {
}

/// Unblock one task by inserting it into the run queue.
///
/// This function does nothing if the task is not in [`TaskState::Blocked`],
/// which means the task is already unblocked by other cores.
pub fn unblock_task(&mut self, task: AxTaskRef, resched: bool) {
let task_id_name = task.id_name();
// Try to change the state of the task from `Blocked` to `Ready`,
// if successful, the task will be put into this run queue,
// otherwise, the task is already unblocked by other cores.
// Note:
// target task can not be insert into the run queue until it finishes its scheduling process.
if self
.inner
.put_task_with_state(task, TaskState::Blocked, resched)
Expand Down Expand Up @@ -275,18 +293,24 @@ impl<'a, G: BaseGuard> CurrentRunQueueRef<'a, G> {
}

/// Migrate the current task to a new run queue matching its CPU affinity and reschedule.
/// This function will put the current task into a **new** run queue with `Ready` state,
/// and reschedule to the next task on **this** run queue.
pub fn migrate_current(&mut self) {
/// This function will spawn a new `migration_task` to perform the migration, which will set
/// current task to `Ready` state and select a proper run queue for it according to its CPU affinity,
/// switch to the migration task immediately after migration task is prepared.
///
/// Note: the ownership if migrating task (which is current task) is handed over to the migration task,
/// before the migration task inserted it into the target run queue.
#[cfg(feature = "smp")]
pub fn migrate_current(&mut self, migration_task: AxTaskRef) {
let curr = &self.current_task;
trace!("task migrate: {}", curr.id_name());
assert!(curr.is_running());

select_run_queue::<NoOp>(curr.as_task_ref())
.inner
.put_task_with_state(curr.clone(), TaskState::Running, false);
// Mark current task's state as `Ready`,
// but, do not put current task to the scheduler of this run queue.
curr.set_state(TaskState::Ready);

self.inner.resched();
// Call `switch_to` to reschedule to the migration task that performs the migration directly.
self.inner.switch_to(crate::current(), migration_task);
}

/// Preempts the current task and reschedules.
Expand Down Expand Up @@ -449,6 +473,25 @@ impl AxRunQueue {
// If the task's state matches `current_state`, set its state to `Ready` and
// put it back to the run queue (except idle task).
if task.transition_state(current_state, TaskState::Ready) && !task.is_idle() {
// If the task is blocked, wait for the task to finish its scheduling process.
// See `unblock_task()` for details.
if current_state == TaskState::Blocked {
// Wait for next task's scheduling process to complete.
// If the owning (remote) CPU is still in the middle of schedule() with
// this task (next task) as prev, wait until it's done referencing the task.
//
// Pairs with the `clear_prev_task_on_cpu()`.
//
// Note:
// 1. This should be placed after the judgement of `TaskState::Blocked,`,
// because the task may have been woken up by other cores.
// 2. This can be placed in the front of `switch_to()`
#[cfg(feature = "smp")]
while task.on_cpu() {
// Wait for the task to finish its scheduling process.
core::hint::spin_loop();
}
}
// TODO: priority
self.scheduler.lock().put_prev_task(task, preempt);
true
Expand Down Expand Up @@ -496,22 +539,6 @@ impl AxRunQueue {
return;
}

// Task must be scheduled atomically, wait for next task's scheduling process to complete.
// If the owning (remote) CPU is still in the middle of schedule() with
// this task (next task) as prev, wait until it's done referencing the task.
//
// Pairs with the `clear_prev_task_on_cpu()`.
//
// Note:
// 1. This should be placed after the judgement of `TaskState::Blocked,`,
// because the task may have been woken up by other cores.
// 2. This can be placed in the front of `switch_to()`
#[cfg(feature = "smp")]
while next_task.on_cpu() {
// Wait for the task to finish its scheduling process.
core::hint::spin_loop();
}

// Claim the task as running, we do this before switching to it
// such that any running task will have this set.
#[cfg(feature = "smp")]
Expand All @@ -521,9 +548,11 @@ impl AxRunQueue {
let prev_ctx_ptr = prev_task.ctx_mut_ptr();
let next_ctx_ptr = next_task.ctx_mut_ptr();

// Store the weak pointer of **prev_task** in **next_task**'s struct.
// Store the weak pointer of **prev_task** in percpu variable `PREV_TASK`.
#[cfg(feature = "smp")]
next_task.set_prev_task(prev_task.as_task_ref());
{
*PREV_TASK.current_ref_mut_raw() = Arc::downgrade(prev_task.as_task_ref());
}

// The strong reference count of `prev_task` will be decremented by 1,
// but won't be dropped until `gc_entry()` is called.
Expand All @@ -537,7 +566,7 @@ impl AxRunQueue {
// Current it's **next_task** running on this CPU, clear the `prev_task`'s `on_cpu` field
// to indicate that it has finished its scheduling process and no longer running on this CPU.
#[cfg(feature = "smp")]
crate::current().clear_prev_task_on_cpu();
clear_prev_task_on_cpu();
}
}
}
Expand Down Expand Up @@ -567,6 +596,29 @@ fn gc_entry() {
}
}

/// The task routine for migrating the current task to the correct CPU.
///
/// It calls `select_run_queue` to get the correct run queue for the task, and
/// then puts the task to the scheduler of target run queue.
#[cfg(feature = "smp")]
pub(crate) fn migrate_entry(migrated_task: AxTaskRef) {
select_run_queue::<kernel_guard::NoPreemptIrqSave>(&migrated_task)
.inner
.scheduler
.lock()
.put_prev_task(migrated_task, false)
}

/// Clear the `on_cpu` field of previous task running on this CPU.
#[cfg(feature = "smp")]
pub(crate) unsafe fn clear_prev_task_on_cpu() {
PREV_TASK
.current_ref_raw()
.upgrade()
.expect("Invalid prev_task pointer or prev_task has been dropped")
.set_on_cpu(false);
}

pub(crate) fn init() {
let cpu_id = this_cpu_id();

Expand Down
Loading

0 comments on commit 1575924

Please sign in to comment.