Unify context list again now that we have a run queue

This simplifies code and ensures that exiting a context will properly
remove it from the list of contexts.
This commit is contained in:
bjorn3
2026-04-17 21:09:02 +02:00
committed by Wildan M
parent af1bc5d5e2
commit 7827cb6c78
11 changed files with 176 additions and 206 deletions
+12 -15
View File
@@ -72,13 +72,7 @@ pub use self::arch::empty_cr3;
// Set of weak references to all contexts available for scheduling. The only strong references are
// the context file descriptors.
static CONTEXTS: Mutex<L2, BTreeSet<ContextRef>> = Mutex::new(BTreeSet::new());
/// Try to get the global free contexts
pub fn free_contexts_try(
token: LockToken<'_, L1>,
) -> Option<MutexGuard<'_, L2, BTreeSet<ContextRef>>> {
CONTEXTS.try_lock(token)
}
static CONTEXTS: RwLock<L2, BTreeSet<ContextRef>> = RwLock::new(BTreeSet::new());
// Actual context store for the scheduler
static RUN_CONTEXTS: Mutex<L1, RunContextData> = Mutex::new(RunContextData::new());
@@ -97,15 +91,13 @@ impl RunContextData {
}
/// Get the global schemes list, const
pub fn contexts(token: LockToken<'_, L0>) -> RwLockReadGuard<'_, L1, BTreeSet<ContextRef>> {
let percpu = PercpuBlock::current();
percpu.contexts.read(token)
pub fn contexts(token: LockToken<'_, L1>) -> RwLockReadGuard<'_, L2, BTreeSet<ContextRef>> {
CONTEXTS.read(token)
}
/// Get per cpu contexts, mutable
pub fn contexts_mut(token: LockToken<'_, L0>) -> RwLockWriteGuard<'_, L1, BTreeSet<ContextRef>> {
let percpu = PercpuBlock::current();
percpu.contexts.write(token)
pub fn contexts_mut(token: LockToken<'_, L1>) -> RwLockWriteGuard<'_, L2, BTreeSet<ContextRef>> {
CONTEXTS.write(token)
}
pub fn run_contexts(token: LockToken<'_, L0>) -> MutexGuard<'_, L1, RunContextData> {
@@ -128,10 +120,12 @@ pub fn init(token: &mut CleanLockToken) {
context.cpu_id = Some(crate::cpu_id());
context.enqueued = false;
let priority = context.prio;
let context_lock = Arc::new(ContextLock::new(context));
let context_ref = ContextRef(Arc::clone(&context_lock));
contexts_mut(token.token()).insert(context_ref);
contexts_mut(token.token().downgrade()).insert(context_ref.clone());
unsafe {
let percpu = PercpuBlock::current();
@@ -139,6 +133,8 @@ pub fn init(token: &mut CleanLockToken) {
.switch_internals
.set_current_context(Arc::clone(&context_lock));
}
run_contexts(token.downgrade()).set[priority].push_back(context_ref);
}
pub fn wakeup_context(context_lock: &Arc<RwLock<L4, Context>>, mut token: LockToken<L0>) {
@@ -176,6 +172,7 @@ pub fn is_current(context: &Arc<ContextLock>) -> bool {
.with_context(|current| Arc::ptr_eq(context, current))
}
#[derive(Clone)]
pub struct ContextRef(pub Arc<ContextLock>);
impl ContextRef {
pub fn upgrade(&self) -> Option<Arc<ContextLock>> {
@@ -224,7 +221,7 @@ pub fn spawn(
let run_ref = ContextRef(Arc::clone(&context_lock));
run_contexts(token.token()).set[20].push_back(run_ref);
contexts_mut(token.token()).insert(context_ref);
contexts_mut(token.token().downgrade()).insert(context_ref);
context_lock.write(token.token()).enqueued = true;
Ok(context_lock)
+4 -26
View File
@@ -3,10 +3,7 @@
//! handling process states and synchronization.
use crate::{
context::{
self, arch, contexts, contexts_mut, free_contexts_try, run_contexts,
ArcContextLockWriteGuard, Context, ContextLock,
},
context::{self, arch, contexts, run_contexts, ArcContextLockWriteGuard, Context, ContextLock},
cpu_set::LogicalCpuId,
cpu_stats::{self, CpuState},
percpu::PercpuBlock,
@@ -176,12 +173,12 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
}
// Alarm (previously in update_runnable)
// TODO: Optimise this somehow
// TODO: Optimise this somehow. Perhaps using a separate timer queue?
let mut wakeups = Vec::new();
{
let current_context = context::current();
let mut contexts_guard = contexts(token.token());
let mut contexts_guard = contexts(token.downgrade());
let (context, mut token) = contexts_guard.token_split();
for context_ref in context.iter().filter_map(|r| r.upgrade()) {
if Arc::ptr_eq(&context_ref, &current_context) {
@@ -214,7 +211,7 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
let was_idle = percpu.stats.add_time(percpu_ms) == CpuState::Idle as u8;
percpu.switch_internals.switch_time.set(switch_time);
let mut switch_context_opt = match select_next_context(
let switch_context_opt = match select_next_context(
token,
percpu,
cpu_id,
@@ -226,25 +223,6 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
Err(early_ret) => return early_ret,
};
if switch_context_opt.is_none() {
let mut this_contexts = contexts_mut(token.token());
let (this_contexts, mut token) = this_contexts.token_split();
if let Some(mut free_contexts) = free_contexts_try(token.token())
&& let Some(context) = free_contexts.pop_last()
{
// Check if we can run this free context immediately
if let Some(next_context) = context.upgrade() {
let mut next_context_guard = unsafe { next_context.write_arc() };
if let UpdateResult::CanSwitch =
unsafe { update_runnable(&mut next_context_guard, cpu_id, switch_time) }
{
switch_context_opt = Some(next_context_guard);
}
}
this_contexts.insert(context);
}
}
// Switch process states, TSS stack pointer, and store new context ID
match switch_context_opt {
Some(mut next_context_guard) => {
+125 -122
View File
@@ -1,5 +1,5 @@
use crate::{
context::{context::SyscallFrame, Context, ContextLock},
context::{context::SyscallFrame, contexts, Context, ContextLock},
memory::{
get_page_info, the_zeroed_frame, Frame, RefCount, RmmA, RmmArch, TableKind, PAGE_SIZE,
},
@@ -20,154 +20,157 @@ pub unsafe fn debugger(target_id: Option<*const ContextLock>, token: &mut CleanL
let old_table = RmmA::table(TableKind::User);
let contexts = crate::percpu::get_all_contexts(token.downgrade());
for context_arc in contexts.iter() {
if target_id.map_or(false, |target_id| Arc::as_ptr(&context_arc) != target_id) {
continue;
}
let context = context_arc.read(token.token());
println!("{:p}: {}", Arc::as_ptr(&context_arc), context.name);
{
let mut contexts = contexts(token.downgrade());
let (contexts, mut token) = contexts.token_split();
for context_arc in contexts.iter().filter_map(|x| x.upgrade()) {
if target_id.map_or(false, |target_id| Arc::as_ptr(&context_arc) != target_id) {
continue;
}
let context = context_arc.read(token.token());
println!("{:p}: {}", Arc::as_ptr(&context_arc), context.name);
let mut mark_frame_use = |frame| {
tree.entry(frame).or_insert((0, false)).0 += 1;
};
let mut mark_frame_use = |frame| {
tree.entry(frame).or_insert((0, false)).0 += 1;
};
match &context.syscall_head {
SyscallFrame::Free(head) => mark_frame_use(head.get()),
SyscallFrame::Used { _frame: head } => mark_frame_use(*head),
SyscallFrame::Dummy => {}
}
match &context.syscall_tail {
SyscallFrame::Free(tail) => mark_frame_use(tail.get()),
SyscallFrame::Used { _frame: tail } => mark_frame_use(*tail),
SyscallFrame::Dummy => {}
}
match &context.syscall_head {
SyscallFrame::Free(head) => mark_frame_use(head.get()),
SyscallFrame::Used { _frame: head } => mark_frame_use(*head),
SyscallFrame::Dummy => {}
}
match &context.syscall_tail {
SyscallFrame::Free(tail) => mark_frame_use(tail.get()),
SyscallFrame::Used { _frame: tail } => mark_frame_use(*tail),
SyscallFrame::Dummy => {}
}
if let Some(sig) = &context.sig {
mark_frame_use(sig.proc_control.get());
mark_frame_use(sig.thread_control.get());
}
if let Some(sig) = &context.sig {
mark_frame_use(sig.proc_control.get());
mark_frame_use(sig.thread_control.get());
}
// TODO: Lock ordering violation
let mut token = unsafe { CleanLockToken::new() };
// TODO: Lock ordering violation
let mut token = unsafe { CleanLockToken::new() };
// Switch to context page table to ensure syscall debug and stack dump will work
if let Some(ref space) = context.addr_space {
let was_new = spaces.insert(
space
.acquire_read(token.downgrade())
.table
.utable
.table()
.phys()
.data(),
);
unsafe {
RmmA::set_table(
TableKind::User,
// Switch to context page table to ensure syscall debug and stack dump will work
if let Some(ref space) = context.addr_space {
let was_new = spaces.insert(
space
.acquire_read(token.downgrade())
.table
.utable
.table()
.phys(),
.phys()
.data(),
);
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
check_page_table_consistency(
&mut space.acquire_write(token.downgrade()),
was_new,
&mut tree,
unsafe {
RmmA::set_table(
TableKind::User,
space
.acquire_read(token.downgrade())
.table
.utable
.table()
.phys(),
);
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
check_page_table_consistency(
&mut space.acquire_write(token.downgrade()),
was_new,
&mut tree,
);
}
}
println!("status: {:?}", context.status);
if !context.status_reason.is_empty() {
println!("reason: {}", context.status_reason);
}
if let Some([a, b, c, d, e, f, g]) = context.current_syscall() {
println!(
"syscall: {}",
crate::syscall::debug::format_call(a, b, c, d, e, f, g)
);
}
}
if let Some(ref addr_space) = context.addr_space {
let addr_space = addr_space.acquire_read(token.downgrade());
if !addr_space.grants.is_empty() {
println!("grants:");
for (base, info) in addr_space.grants.iter() {
let size = info.page_count() * PAGE_SIZE;
println!("status: {:?}", context.status);
if !context.status_reason.is_empty() {
println!("reason: {}", context.status_reason);
}
if let Some([a, b, c, d, e, f, g]) = context.current_syscall() {
println!(
"syscall: {}",
crate::syscall::debug::format_call(a, b, c, d, e, f, g)
);
}
if let Some(ref addr_space) = context.addr_space {
let addr_space = addr_space.acquire_read(token.downgrade());
if !addr_space.grants.is_empty() {
println!("grants:");
for (base, info) in addr_space.grants.iter() {
let size = info.page_count() * PAGE_SIZE;
let flags = format_args!(
"{}{}{}{}",
if info.flags().has_user() { "u" } else { "k" },
if info.flags().has_present() { "r" } else { "-" },
if info.flags().has_write() { "w" } else { "-" },
if info.flags().has_execute() { "x" } else { "-" },
);
let flags = format_args!(
"{}{}{}{}",
if info.flags().has_user() { "u" } else { "k" },
if info.flags().has_present() { "r" } else { "-" },
if info.flags().has_write() { "w" } else { "-" },
if info.flags().has_execute() { "x" } else { "-" },
);
#[cfg(target_arch = "aarch64")]
println!(
" virt 0x{:016x}:0x{:016x} {} size 0x{:08x} {:?}",
base.start_address().data(),
base.next_by(info.page_count() - 1).start_address().data() + 0xFFF,
flags,
size,
info.provider,
);
#[cfg(target_arch = "aarch64")]
println!(
" virt 0x{:016x}:0x{:016x} {} size 0x{:08x} {:?}",
base.start_address().data(),
base.next_by(info.page_count() - 1).start_address().data() + 0xFFF,
flags,
size,
info.provider,
);
// FIXME riscv64 implementation
// FIXME riscv64 implementation
#[cfg(target_arch = "x86")]
println!(
" virt 0x{:08x}:0x{:08x} {} size 0x{:08x} {:?}",
base.start_address().data(),
base.next_by(info.page_count()).start_address().data() + 0xFFF,
flags,
size,
info.provider,
);
#[cfg(target_arch = "x86")]
println!(
" virt 0x{:08x}:0x{:08x} {} size 0x{:08x} {:?}",
base.start_address().data(),
base.next_by(info.page_count()).start_address().data() + 0xFFF,
flags,
size,
info.provider,
);
#[cfg(target_arch = "x86_64")]
println!(
" virt 0x{:016x}:0x{:016x} {} size 0x{:08x} {:?}",
base.start_address().data(),
base.start_address().data() + size - 1,
flags,
size,
info.provider,
);
#[cfg(target_arch = "x86_64")]
println!(
" virt 0x{:016x}:0x{:016x} {} size 0x{:08x} {:?}",
base.start_address().data(),
base.start_address().data() + size - 1,
flags,
size,
info.provider,
);
}
}
}
}
if let Some(regs) = context.regs() {
println!("regs:");
regs.dump();
if let Some(regs) = context.regs() {
println!("regs:");
regs.dump();
#[cfg(target_arch = "aarch64")]
dump_stack(&*context, regs.iret.sp_el0);
#[cfg(target_arch = "aarch64")]
dump_stack(&*context, regs.iret.sp_el0);
// FIXME riscv64 implementation
// FIXME riscv64 implementation
#[cfg(target_arch = "x86")]
dump_stack(&*context, regs.iret.esp);
#[cfg(target_arch = "x86")]
dump_stack(&*context, regs.iret.esp);
#[cfg(target_arch = "x86_64")]
{
unsafe {
x86::bits64::rflags::stac();
}
dump_stack(&*context, regs.iret.rsp);
unsafe {
x86::bits64::rflags::clac();
#[cfg(target_arch = "x86_64")]
{
unsafe {
x86::bits64::rflags::stac();
}
dump_stack(&*context, regs.iret.rsp);
unsafe {
x86::bits64::rflags::clac();
}
}
}
// Switch to original page table
unsafe { RmmA::set_table(TableKind::User, old_table) };
println!();
}
// Switch to original page table
unsafe { RmmA::set_table(TableKind::User, old_table) };
println!();
}
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
crate::scheme::proc::foreach_addrsp(token, |addrsp, mut token| {
+2 -22
View File
@@ -1,5 +1,4 @@
use alloc::{
collections::BTreeSet,
sync::{Arc, Weak},
vec::Vec,
};
@@ -13,13 +12,11 @@ use syscall::PtraceFlags;
use crate::{
arch::device::ArchPercpuMisc,
context::{
empty_cr3, memory::AddrSpaceWrapper, switch::ContextSwitchPercpu, ContextLock, ContextRef,
},
context::{empty_cr3, memory::AddrSpaceWrapper, switch::ContextSwitchPercpu},
cpu_set::{LogicalCpuId, MAX_CPU_COUNT},
cpu_stats::{CpuStats, CpuStatsData},
ptrace::Session,
sync::{CleanLockToken, LockToken, RwLock, L1},
sync::CleanLockToken,
syscall::debug::SyscallDebugInfo,
};
@@ -50,7 +47,6 @@ pub struct PercpuBlock {
pub misc_arch_info: crate::arch::device::ArchPercpuMisc,
pub stats: CpuStats,
pub contexts: RwLock<L1, BTreeSet<ContextRef>>,
}
static ALL_PERCPU_BLOCKS: [AtomicPtr<PercpuBlock>; MAX_CPU_COUNT as usize] =
@@ -74,21 +70,6 @@ pub fn get_all_stats() -> Vec<(LogicalCpuId, CpuStatsData)> {
res
}
/// Get copy of all cpu ref contexts
pub fn get_all_contexts(mut token: LockToken<'_, L1>) -> Vec<Arc<ContextLock>> {
let mut all_contexts = Vec::new();
for block in ALL_PERCPU_BLOCKS
.iter()
.filter_map(|block| unsafe { block.load(Ordering::Relaxed).as_ref() })
{
// TODO: When load balancer implemented, contexts need to be locked altogether
// TODO: Lock token violation, downgrade this to L2 later
let contexts = unsafe { &block.contexts.reupgradeable_read(token.token()) };
all_contexts.extend(contexts.iter().filter_map(|x| x.upgrade()));
}
all_contexts
}
// PercpuBlock::current() is implemented somewhere in the arch-specific modules
pub fn shootdown_tlb_ipi(target: Option<LogicalCpuId>) {
@@ -219,7 +200,6 @@ impl PercpuBlock {
misc_arch_info: ArchPercpuMisc::default(),
stats: CpuStats::default(),
contexts: RwLock::new(BTreeSet::new()),
}
}
}
+4 -3
View File
@@ -1,7 +1,7 @@
use alloc::{string::String, vec::Vec};
use core::fmt::Write;
use crate::{percpu, sync::CleanLockToken, syscall::error::Result};
use crate::{context::contexts, sync::CleanLockToken, syscall::error::Result};
pub fn resource(token: &mut CleanLockToken) -> Result<Vec<u8>> {
let mut string = String::new();
@@ -9,8 +9,9 @@ pub fn resource(token: &mut CleanLockToken) -> Result<Vec<u8>> {
{
let mut rows = Vec::new();
{
let contexts = percpu::get_all_contexts(token.downgrade());
for context_lock in contexts {
let mut contexts = contexts(token.downgrade());
let (contexts, mut token) = contexts.token_split();
for context_lock in contexts.iter().filter_map(|x| x.upgrade()) {
let context = context_lock.read(token.token());
rows.push((context.pid, context.name, context.status_reason));
}
+5 -4
View File
@@ -5,7 +5,7 @@ use alloc::{
};
use core::fmt::Write;
use crate::{context, percpu, sync::CleanLockToken, syscall::error::Result};
use crate::{context, context::contexts, sync::CleanLockToken, syscall::error::Result};
pub fn resource(token: &mut CleanLockToken) -> Result<Vec<u8>> {
let mut string = format!(
@@ -15,8 +15,9 @@ pub fn resource(token: &mut CleanLockToken) -> Result<Vec<u8>> {
let mut rows = Vec::new();
{
let contexts = percpu::get_all_contexts(token.downgrade());
for context_ref in contexts {
let mut contexts = contexts(token.downgrade());
let (contexts, mut token) = contexts.token_split();
for context_ref in contexts.iter().filter_map(|x| x.upgrade()) {
let context = context_ref.read(token.token());
let addr_space = context.addr_space().map(|a| a.clone());
@@ -35,7 +36,7 @@ pub fn resource(token: &mut CleanLockToken) -> Result<Vec<u8>> {
let heap = match addr_space {
Ok(addr_space) => {
let addr_space_guard = addr_space.acquire_read(token.downgrade());
let addr_space_guard = addr_space.acquire_read(token.token());
let mut private_memory = 0;
let mut shared_memory = 0;
// TODO: All user programs must have some grant in order for executable memory to even
+4 -3
View File
@@ -1,7 +1,6 @@
use crate::{
alloc::string::ToString,
context::{file::LockedFileDescription, memory::AddrSpaceWrapper},
percpu,
context::{contexts, file::LockedFileDescription, memory::AddrSpaceWrapper},
scheme::{self, handles, KernelSchemes},
sync::CleanLockToken,
syscall::error::Result,
@@ -37,7 +36,9 @@ pub fn resource(token: &mut CleanLockToken) -> Result<Vec<u8>> {
let mut schemes_guard = handles().read(token.token());
let (schemes, mut token) = schemes_guard.token_split();
'contexts: for context in percpu::get_all_contexts(token.token()) {
let mut contexts = contexts(token.token());
let (contexts, mut token) = contexts.token_split();
'contexts: for context in contexts.iter().filter_map(|x| x.upgrade()) {
let mut context_guard = context.read(token.token());
let (context, token) = context_guard.token_split();
let mut files_guard = context.files.read(token);
+4 -2
View File
@@ -4,7 +4,7 @@ use crate::{
memory::{Grant, PageSpan},
},
memory::PAGE_SIZE,
percpu, scheme,
scheme,
sync::CleanLockToken,
syscall::{
error::Result,
@@ -22,7 +22,9 @@ fn inner(fpath_user: UserSliceRw, token: &mut CleanLockToken) -> Result<Vec<u8>>
{
let mut rows = Vec::new();
{
for context_ref in percpu::get_all_contexts(token.downgrade()) {
let mut contexts = context::contexts(token.downgrade());
let (contexts, mut token) = contexts.token_split();
for context_ref in contexts.iter().filter_map(|x| x.upgrade()) {
let mut current = context_ref.read(token.token());
let (context, mut token) = current.token_split();
rows.push((
+11 -6
View File
@@ -1,9 +1,9 @@
use core::fmt::Write as _;
use crate::{
context::Status,
context::{contexts, Status},
cpu_stats::{get_context_switch_count, get_contexts_count, irq_counts},
percpu::{self, get_all_stats},
percpu::get_all_stats,
sync::CleanLockToken,
syscall::error::Result,
time::START,
@@ -76,10 +76,15 @@ fn get_contexts_stats(token: &mut CleanLockToken) -> (u64, u64) {
let mut running = 0;
let mut blocked = 0;
let statuses = percpu::get_all_contexts(token.downgrade())
.iter()
.map(|context| context.read(token.token()).status.clone())
.collect::<Vec<_>>();
let statuses = {
let mut contexts = contexts(token.downgrade());
let (contexts, mut token) = contexts.token_split();
contexts
.iter()
.filter_map(|x| x.upgrade())
.map(|context| context.read(token.token()).status.clone())
.collect::<Vec<_>>()
};
for status in statuses {
if matches!(status, Status::Runnable) {
+4 -2
View File
@@ -2,7 +2,7 @@ use alloc::{string::String, vec::Vec};
use core::fmt::Write;
use crate::{
percpu,
context::contexts,
sync::CleanLockToken,
syscall::{self, error::Result},
};
@@ -13,7 +13,9 @@ pub fn resource(token: &mut CleanLockToken) -> Result<Vec<u8>> {
{
let mut rows = Vec::new();
{
for context_ref in percpu::get_all_contexts(token.downgrade()) {
let mut contexts = contexts(token.downgrade());
let (contexts, mut token) = contexts.token_split();
for context_ref in contexts.iter().filter_map(|x| x.upgrade()) {
let context = context_ref.read(token.token());
rows.push((context.pid, context.name, context.current_syscall()));
}
+1 -1
View File
@@ -68,7 +68,7 @@ pub fn exit_this_context(excp: Option<syscall::Exception>, token: &mut CleanLock
);
}
{
if !context::contexts_mut(token.token()).remove(&ContextRef(context_lock)) {
if !context::contexts_mut(token.downgrade()).remove(&ContextRef(context_lock)) {
#[cfg(feature = "drop_panic")]
{
panic!("This context is not in the cpu")