diff --git a/header/src/lib.rs b/header/src/lib.rs index 98fdc634..0b11a268 100644 --- a/header/src/lib.rs +++ b/header/src/lib.rs @@ -84,6 +84,9 @@ pub mod syscalls { MqTimedReceive, MqGetSetAttr, Ioctl, + SchedGetPriorityMax, + SchedGetPriorityMin, + SchedRrGetInterval, LastNR, } } diff --git a/kernel/src/scheduler/global_scheduler.rs b/kernel/src/scheduler/global_scheduler.rs index 3c08a26d..c54ea795 100644 --- a/kernel/src/scheduler/global_scheduler.rs +++ b/kernel/src/scheduler/global_scheduler.rs @@ -174,23 +174,40 @@ pub fn remove_from_ready_queue(t: &ThreadNode) -> bool { remove_from_ready_queue_inner(&mut tbl, t) } -pub fn update_ready_thread_priority( - t: &ThreadNode, - new_priority: ThreadPriority, -) -> Result<(), Uint> { +pub fn update_thread_priority_safe(t: &ThreadNode, newprio: ThreadPriority) { let mut tbl = unsafe { READY_TABLE.assume_init_ref().irqsave_lock() }; - if !remove_from_ready_queue_inner(&mut tbl, t) { - let state = t.state(); - return Err(state); + + let mut removed_from_ready = false; + let priority = t.priority(); + if priority <= MAX_THREAD_PRIORITY { + let q = &mut tbl.tables[priority as usize]; + let removed = q.remove_if(|e| ThreadNode::as_ptr(t) == e as *const _); + if removed.is_some() { + removed_from_ready = true; + if q.is_empty() { + tbl.clear_active_queue(priority as u32); + } + } } - let mut thread_guard = t.lock(); - thread_guard.set_origin_priority(new_priority); - thread_guard.set_priority(new_priority); - drop(thread_guard); + if removed_from_ready { + // Enqueued: update fields then requeue under the same ready-table lock. + { + let mut g = t.lock(); + g.set_origin_priority(newprio); + g.set_priority(newprio); + } + let _ = queue_ready_thread_inner(&mut tbl, t.clone()); + return; + } - if queue_ready_thread_inner(&mut tbl, t.clone()) { - return Ok(()); + // Not enqueued: drop runqueue lock and update fields only. + drop(tbl); + let mut g = t.lock(); + // if retired, do not change priority + if g.state() == thread::RETIRED { + return; } - Err(thread::RUNNING) + g.set_origin_priority(newprio); + g.set_priority(newprio); } diff --git a/kernel/src/syscall_handlers/mod.rs b/kernel/src/syscall_handlers/mod.rs index f7233c7b..7e8e1ada 100644 --- a/kernel/src/syscall_handlers/mod.rs +++ b/kernel/src/syscall_handlers/mod.rs @@ -41,6 +41,7 @@ use libc::{ sigset_t, size_t, sockaddr, socklen_t, timespec, EBUSY, EINVAL, ESRCH, }; +pub const SCHED_RR: c_int = 1; #[cfg(not(enable_vfs))] mod vfs_syscalls { use super::*; @@ -334,7 +335,7 @@ get_sched_param(tid: usize) -> c_long { let Some(target) = target else { return -(ESRCH as c_long); }; - target.priority() as c_long + (config::MAX_THREAD_PRIORITY - (target.priority() as crate::types::ThreadPriority)) as c_long }); define_syscall_handler!( @@ -342,37 +343,21 @@ set_sched_param(tid: usize, prio: c_int) -> c_long { if prio < 0 || (prio as u32) > (config::MAX_THREAD_PRIORITY as u32) { return -(EINVAL as c_long); } - let p = prio as crate::types::ThreadPriority; - let mut was_ready = false; - let target = thread::GlobalQueueVisitor::find_if(|t| { - if thread::Thread::id(t) == tid { - was_ready = t.state() == thread::READY; - if !was_ready { - let mut w = t.lock(); - w.set_origin_priority(p); - w.set_priority(p); - } - true - } else { - false - } - }); + // convert posix priority to kernel priority + let p = config::MAX_THREAD_PRIORITY - (prio as crate::types::ThreadPriority); + let target = thread::GlobalQueueVisitor::find_if(|t| thread::Thread::id(t) == tid); let Some(target) = target else { return -(ESRCH as c_long); }; - + if target.lock().priority() == p { + return 0; + } let preempt_guard = thread::Thread::try_preempt_me(); - let ret = if was_ready { - match scheduler::update_ready_thread_priority(&target, p) { - Ok(()) => 0, - Err(_) => -(EBUSY as c_long), - } - } else { - 0 - }; + + scheduler::update_thread_priority_safe(&target, p); drop(preempt_guard); - ret + 0 }); define_syscall_handler!( @@ -505,6 +490,39 @@ define_syscall_handler!(sched_yield() -> c_long { scheduler::yield_me(); 0 }); + +define_syscall_handler!(sched_get_priority_max(policy: c_int) -> c_long { + // Only SCHED_RR is supported. + if policy != SCHED_RR { + return -(libc::EINVAL as c_long); + } + config::MAX_THREAD_PRIORITY as c_long +}); + +define_syscall_handler!(sched_get_priority_min(policy: c_int) -> c_long { + // Only SCHED_RR is supported. + if policy != SCHED_RR { + return -(libc::EINVAL as c_long); + } + 0 +}); + +define_syscall_handler!(sched_rr_get_interval(pid: c_int, interval: *mut timespec) -> c_long { + let _ = pid; + if interval.is_null() { + return -(libc::EINVAL as c_long); + } + + unsafe { + (*interval).tv_sec = 0; + let ns: i64 = (time::tick_to_millisecond(blueos_kconfig::CONFIG_ROBIN_SLICE as usize) + as i64) + .saturating_mul(1_000_000); + let clamped: i64 = core::cmp::min(ns, 999_999_999); + (*interval).tv_nsec = clamped as c_int; + } + 0 +}); define_syscall_handler!( rmdir(path: *const c_char) -> c_int { vfs_syscalls::rmdir(path) @@ -896,6 +914,9 @@ syscall_table! { (MqTimedReceive, mq_timedreceive), (MqGetSetAttr, mq_getsetattr), (Ioctl, ioctl), + (SchedGetPriorityMax, sched_get_priority_max), + (SchedGetPriorityMin, sched_get_priority_min), + (SchedRrGetInterval, sched_rr_get_interval), } #[cfg(not(enable_syscall))]