Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
98 changes: 87 additions & 11 deletions os/StarryOS/kernel/src/syscall/task/schedule.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use alloc::{sync::Arc, vec::Vec};

use ax_errno::{AxError, AxResult};
use ax_runtime::hal::time::TimeValue;
use ax_runtime::hal::{self, time::TimeValue};
use ax_task::{
AxCpuMask, current,
future::{block_on, interruptible, sleep},
};
use bytemuck::{Pod, Zeroable};
use linux_raw_sys::general::{
__kernel_clockid_t, CLOCK_MONOTONIC, CLOCK_REALTIME, PRIO_PGRP, PRIO_PROCESS, PRIO_USER,
SCHED_RR, TIMER_ABSTIME, timespec,
SCHED_BATCH, SCHED_FIFO, SCHED_IDLE, SCHED_NORMAL, SCHED_RR, TIMER_ABSTIME, timespec,
};
use starry_vm::{VmMutPtr, VmPtr, vm_load, vm_write_slice};

Expand All @@ -20,6 +21,12 @@ use crate::{
time::TimeValueLike,
};

#[repr(C)]
#[derive(Copy, Clone, Pod, Zeroable)]
struct SchedParam {
sched_priority: i32,
}

pub fn sys_sched_yield() -> AxResult<isize> {
ax_task::yield_now();
Ok(0)
Expand All @@ -43,7 +50,7 @@ pub fn sys_nanosleep(req: *const timespec, rem: *mut timespec) -> AxResult<isize
let req = unsafe { req.vm_read_uninit()?.assume_init() }.try_into_time_value()?;
debug!("sys_nanosleep <= req: {req:?}");

let actual = sleep_impl(ax_runtime::hal::time::monotonic_time, req);
let actual = sleep_impl(hal::time::monotonic_time, req);

if let Some(diff) = req.checked_sub(actual) {
debug!("sys_nanosleep => rem: {diff:?}");
Expand All @@ -63,8 +70,8 @@ pub fn sys_clock_nanosleep(
rem: *mut timespec,
) -> AxResult<isize> {
let clock = match clock_id as u32 {
CLOCK_REALTIME => ax_runtime::hal::time::wall_time,
CLOCK_MONOTONIC => ax_runtime::hal::time::monotonic_time,
CLOCK_REALTIME => hal::time::wall_time,
CLOCK_MONOTONIC => hal::time::monotonic_time,
_ => {
warn!("Unsupported clock_id: {clock_id}");
return Err(AxError::InvalidInput);
Expand Down Expand Up @@ -94,7 +101,7 @@ pub fn sys_clock_nanosleep(
}

pub fn sys_sched_getaffinity(pid: i32, cpusetsize: usize, user_mask: *mut u8) -> AxResult<isize> {
if cpusetsize * 8 < ax_runtime::hal::cpu_num() {
if cpusetsize * 8 < hal::cpu_num() {
return Err(AxError::InvalidInput);
}

Expand All @@ -107,12 +114,32 @@ pub fn sys_sched_getaffinity(pid: i32, cpusetsize: usize, user_mask: *mut u8) ->
Ok(mask_bytes.len() as _)
}

pub fn check_sched_permission(pid: i32) -> AxResult<()> {
let caller = current().as_thread().cred();
let task = get_task_by_sched_pid(pid)?;
if task.id() == current().id() {
return Ok(());
}
let target_proc = get_process_data(pid as u32)?;
let target_cred = process_cred(&target_proc)?;
if caller.has_cap_sys_nice()
|| caller.euid == target_cred.uid
|| caller.euid == target_cred.euid
{
Ok(())
} else {
Err(AxError::OperationNotPermitted)
}
}

pub fn sys_sched_setaffinity(pid: i32, cpusetsize: usize, user_mask: *const u8) -> AxResult<isize> {
let size = cpusetsize.min(ax_runtime::hal::cpu_num().div_ceil(8));
check_sched_permission(pid)?;
let task = get_task_by_sched_pid(pid)?;
let size = cpusetsize.min(hal::cpu_num().div_ceil(8));
let user_mask = vm_load(user_mask, size)?;
let mut cpu_mask = AxCpuMask::new();

for i in 0..(size * 8).min(ax_runtime::hal::cpu_num()) {
for i in 0..(size * 8).min(hal::cpu_num()) {
if user_mask[i / 8] & (1 << (i % 8)) != 0 {
cpu_mask.set(i, true);
}
Expand All @@ -121,8 +148,6 @@ pub fn sys_sched_setaffinity(pid: i32, cpusetsize: usize, user_mask: *const u8)
if cpu_mask.is_empty() {
return Err(AxError::InvalidInput);
}

let task = get_task_by_sched_pid(pid)?;
if task.id() == current().id() {
ax_task::set_current_affinity(cpu_mask);
} else {
Expand All @@ -141,14 +166,65 @@ fn get_task_by_sched_pid(pid: i32) -> AxResult<ax_task::AxTaskRef> {
}

pub fn sys_sched_getscheduler(_pid: i32) -> AxResult<isize> {
Ok(SCHED_RR as _)
let task = get_task_by_sched_pid(_pid)?;
Ok(task.sched_policy() as isize)
}

pub fn sys_sched_setscheduler(_pid: i32, _policy: i32, _param: *const ()) -> AxResult<isize> {
check_sched_permission(_pid)?;
let task = get_task_by_sched_pid(_pid)?;
let caller = current().as_thread().cred();
if _param.is_null() {
return Err(AxError::InvalidInput);
}
let user_param = vm_load::<SchedParam>(_param.cast(), 1)?;
let user_param = user_param[0];
let mut policy = _policy as u32;
const SCHED_RESET_ON_FORK: u32 = 0x40000000;
let _reset_on_fork = (policy & SCHED_RESET_ON_FORK) != 0;
policy &= !SCHED_RESET_ON_FORK;
let prio = user_param.sched_priority;
match policy {
SCHED_NORMAL | SCHED_FIFO | SCHED_RR | SCHED_BATCH | SCHED_IDLE => {}
_ => return Err(AxError::InvalidInput),
}
match policy {
SCHED_NORMAL | SCHED_BATCH | SCHED_IDLE => {
if prio != 0 {
return Err(AxError::InvalidInput);
}
}
SCHED_FIFO | SCHED_RR => {
if !(1..=99).contains(&prio) {
return Err(AxError::InvalidInput);
}
if !caller.has_cap_sys_nice() {
return Err(AxError::OperationNotPermitted);
}
}
_ => unreachable!(),
}
task.set_sched_policy(policy as i32);
task.set_sched_priority(prio);
Ok(0)
}

pub fn sys_sched_getparam(_pid: i32, _param: *mut ()) -> AxResult<isize> {
let task = get_task_by_sched_pid(_pid)?;
if _param.is_null() {
return Err(AxError::InvalidInput);
}
let param = SchedParam {
sched_priority: task.sched_priority(),
};
let ptr = _param as *mut SchedParam;
unsafe {
let bytes = core::slice::from_raw_parts(
&param as *const SchedParam as *const u8,
core::mem::size_of::<SchedParam>(),
);
vm_write_slice(ptr as *mut u8, bytes)?;
}
Ok(0)
}

Expand Down
28 changes: 28 additions & 0 deletions os/arceos/modules/axtask/src/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ pub struct TaskInner {
/// CPU affinity mask.
cpumask: SpinNoIrq<AxCpuMask>,

/// Scheduling policy of the task.
sched_policy: AtomicI32,

/// Scheduling priority of the task.
sched_priority: AtomicI32,

/// Mark whether the task is in the wait queue.
in_wait_queue: AtomicBool,

Expand Down Expand Up @@ -264,6 +270,26 @@ impl TaskInner {
*self.cpumask.lock() = cpumask
}

#[inline]
pub fn sched_policy(&self) -> i32 {
self.sched_policy.load(Ordering::Acquire)
}

#[inline]
pub fn set_sched_policy(&self, policy: i32) {
self.sched_policy.store(policy, Ordering::Release)
}

#[inline]
pub fn sched_priority(&self) -> i32 {
self.sched_priority.load(Ordering::Acquire)
}

#[inline]
pub fn set_sched_priority(&self, prio: i32) {
self.sched_priority.store(prio, Ordering::Release)
}

/// Polls whether the task has been interrupted.
#[inline]
pub fn poll_interrupt(&self, cx: &Context) -> Poll<()> {
Expand Down Expand Up @@ -324,6 +350,8 @@ impl TaskInner {
state: AtomicU8::new(TaskState::Ready as u8),
// By default, the task is allowed to run on all CPUs.
cpumask: SpinNoIrq::new(crate::api::cpu_mask_full()),
sched_policy: AtomicI32::new(0),
sched_priority: AtomicI32::new(0),
in_wait_queue: AtomicBool::new(false),
#[cfg(feature = "irq")]
timer_ticket_id: AtomicU64::new(0),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
cmake_minimum_required(VERSION 3.20)
project(test-sched-family C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)
add_executable(test-sched-family src/main.c)
target_include_directories(test-sched-family PRIVATE src)
target_compile_options(test-sched-family PRIVATE -Wall -Wextra -Werror)
install(TARGETS test-sched-family RUNTIME DESTINATION usr/bin)
Loading