WIP: Replace ContextId with direct Arcs.

This commit is contained in:
4lDO2
2024-07-12 19:27:11 +02:00
parent 038ff03996
commit 80fe891c6e
30 changed files with 408 additions and 558 deletions
+1 -1
View File
@@ -104,7 +104,7 @@ impl super::Context {
pub fn set_userspace_io_allowed(&mut self, allowed: bool) {
self.arch.userspace_io_allowed = allowed;
if self.id == super::context_id() {
if self.is_current_context() {
unsafe {
crate::gdt::set_userspace_io_allowed(allowed);
}
+1 -1
View File
@@ -114,7 +114,7 @@ impl super::Context {
pub fn set_userspace_io_allowed(&mut self, allowed: bool) {
self.arch.userspace_io_allowed = allowed;
if self.cid == super::current_cid() {
if self.is_current_context() {
unsafe {
crate::gdt::set_userspace_io_allowed(crate::gdt::pcr(), allowed);
}
+10 -14
View File
@@ -21,15 +21,11 @@ use crate::{
use crate::syscall::error::{Error, Result, EAGAIN, ESRCH};
/// Unique identifier for a context (i.e. `pid`).
use ::core::sync::atomic::AtomicUsize;
use super::{
empty_cr3,
memory::{AddrSpaceWrapper, GrantFileRef},
process::{Process, ProcessId},
};
int_like!(ContextId, AtomicContextId, usize, AtomicUsize);
/// The status of a context - used for scheduling
/// See `syscall::process::waitpid` and the `sync` module for examples of usage
@@ -131,8 +127,6 @@ impl Eq for WaitpidKey {}
/// A context, which identifies either a process or a thread
#[derive(Debug)]
pub struct Context {
/// The internal context ID of this context
pub cid: ContextId,
/// The process ID of this context
pub pid: ProcessId,
/// Process state shared with other threads
@@ -207,9 +201,8 @@ pub struct SignalState {
}
impl Context {
pub fn new(cid: ContextId, pid: ProcessId, process: Arc<RwLock<Process>>) -> Result<Context> {
pub fn new(pid: ProcessId, process: Arc<RwLock<Process>>) -> Result<Context> {
let this = Context {
cid,
pid,
process,
sig: None,
@@ -362,6 +355,10 @@ impl Context {
}
}
pub fn is_current_context(&self) -> bool {
self.running && self.cpu_id == Some(crate::cpu_id())
}
pub fn addr_space(&self) -> Result<&Arc<AddrSpaceWrapper>> {
self.addr_space.as_ref().ok_or(Error::new(ESRCH))
}
@@ -375,7 +372,7 @@ impl Context {
return addr_space;
};
if self.cid == super::current_cid() {
if self.is_current_context() {
// TODO: Share more code with context::arch::switch_to.
let this_percpu = PercpuBlock::current();
@@ -474,7 +471,7 @@ impl BorrowedHtBuf {
pub fn head() -> Result<Self> {
Ok(Self {
inner: Some(
context::current()?
context::current()
.write()
.syscall_head
.take()
@@ -486,7 +483,7 @@ impl BorrowedHtBuf {
pub fn tail() -> Result<Self> {
Ok(Self {
inner: Some(
context::current()?
context::current()
.write()
.syscall_tail
.take()
@@ -545,9 +542,8 @@ impl BorrowedHtBuf {
}
impl Drop for BorrowedHtBuf {
fn drop(&mut self) {
let Ok(context) = context::current() else {
return;
};
let context = context::current();
let Some(inner) = self.inner.take() else {
return;
};
-169
View File
@@ -1,169 +0,0 @@
use alloc::{collections::BTreeMap, sync::Arc};
use spin::RwLock;
use spinning_top::RwSpinlock;
use super::{
context::{Context, ContextId, Kstack},
memory::AddrSpaceWrapper,
process::{new_process, Process, ProcessId, ProcessInfo},
};
use crate::{
interrupt::InterruptStack,
scheme::SchemeNamespace,
syscall::error::{Error, Result, EAGAIN},
};
/// Context list type
pub struct ContextList {
// Using a BTreeMap for it's range method
map: BTreeMap<ContextId, Arc<RwSpinlock<Context>>>,
next_id: usize,
}
impl ContextList {
/// Create a new context list.
pub const fn new() -> Self {
ContextList {
map: BTreeMap::new(),
next_id: 1,
}
}
/// Get the nth context.
pub fn get(&self, id: ContextId) -> Option<&Arc<RwSpinlock<Context>>> {
self.map.get(&id)
}
/// Get the current context.
pub fn current(&self) -> Option<&Arc<RwSpinlock<Context>>> {
self.map.get(&super::current_cid())
}
pub fn iter(
&self,
) -> ::alloc::collections::btree_map::Iter<ContextId, Arc<RwSpinlock<Context>>> {
self.map.iter()
}
pub fn range(
&self,
range: impl core::ops::RangeBounds<ContextId>,
) -> ::alloc::collections::btree_map::Range<'_, ContextId, Arc<RwSpinlock<Context>>> {
self.map.range(range)
}
pub(crate) fn insert_context_raw(
&mut self,
cid: ContextId,
pid: ProcessId,
process: Arc<RwLock<Process>>,
) -> Result<&Arc<RwSpinlock<Context>>> {
assert!(self
.map
// TODO
.insert(
cid,
Arc::new(RwSpinlock::new(Context::new(cid, pid, process)?))
)
.is_none());
Ok(self
.map
.get(&cid)
.expect("Failed to insert new context. ID is out of bounds."))
}
/// Create a new context.
pub fn new_context(
&mut self,
process: Arc<RwLock<Process>>,
) -> Result<&Arc<RwSpinlock<Context>>> {
let pid = process.read().pid;
// Zero is not a valid context ID, therefore add 1.
//
// FIXME: Ensure the number of CPUs can't switch between new_context calls.
let min = crate::cpu_count() as usize + 1;
self.next_id = core::cmp::max(self.next_id, min);
if self.next_id >= super::CONTEXT_MAX_CONTEXTS {
self.next_id = min;
}
while self.map.contains_key(&ContextId::from(self.next_id)) {
self.next_id += 1;
}
if self.next_id >= super::CONTEXT_MAX_CONTEXTS {
return Err(Error::new(EAGAIN));
}
let id = ContextId::from(self.next_id);
self.next_id += 1;
self.insert_context_raw(id, pid, process)
}
/// Spawn a context from a function.
pub fn spawn(
&mut self,
userspace_allowed: bool,
process: Arc<RwLock<Process>>,
func: extern "C" fn(),
) -> Result<&Arc<RwSpinlock<Context>>> {
let stack = Kstack::new()?;
let context_lock = self.new_context(Arc::clone(&process))?;
process.write().threads.push(Arc::downgrade(&context_lock));
{
let mut context = context_lock.write();
let _ = context.set_addr_space(Some(AddrSpaceWrapper::new()?));
let mut stack_top = stack.initial_top();
const INT_REGS_SIZE: usize = core::mem::size_of::<crate::interrupt::InterruptStack>();
if userspace_allowed {
unsafe {
// Zero-initialize InterruptStack registers.
stack_top = stack_top.sub(INT_REGS_SIZE);
stack_top.write_bytes(0_u8, INT_REGS_SIZE);
(&mut *stack_top.cast::<InterruptStack>()).init();
}
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
unsafe {
if userspace_allowed {
stack_top = stack_top.sub(core::mem::size_of::<usize>());
stack_top
.cast::<usize>()
.write(crate::interrupt::syscall::enter_usermode as usize);
}
stack_top = stack_top.sub(core::mem::size_of::<usize>());
stack_top.cast::<usize>().write(func as usize);
}
#[cfg(target_arch = "aarch64")]
unsafe {
context
.arch
.set_lr(crate::interrupt::syscall::enter_usermode as usize);
context.arch.set_x28(func as usize);
context.arch.set_context_handle();
}
context.arch.set_stack(stack_top as usize);
context.kstack = Some(stack);
context.userspace = userspace_allowed;
}
Ok(context_lock)
}
pub fn remove(&mut self, id: ContextId) -> Option<Arc<RwSpinlock<Context>>> {
self.map.remove(&id)
}
}
+1 -1
View File
@@ -2712,7 +2712,7 @@ fn correct_inner<'l>(
.request_fmap(scheme_number, offset, 1, flags)
.unwrap();
let context_lock = super::current().map_err(|_| PfError::NonfatalInternalError)?;
let context_lock = crate::context::current();
context_lock
.write()
.hard_block(HardBlockedReason::AwaitingMmap { file_ref });
+137 -40
View File
@@ -2,23 +2,33 @@
//!
//! For resources on contexts, please consult [wikipedia](https://en.wikipedia.org/wiki/Context_switch) and [osdev](https://wiki.osdev.org/Context_Switching)
use alloc::{borrow::Cow, sync::Arc, vec::Vec};
use alloc::{
borrow::Cow,
collections::BTreeSet,
sync::{Arc, Weak},
vec::Vec,
};
use spin::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard};
use spinning_top::RwSpinlock;
use syscall::ENOMEM;
use crate::{
context::memory::AddrSpaceWrapper,
cpu_set::LogicalCpuSet,
interrupt::InterruptStack,
paging::{RmmA, RmmArch, TableKind},
percpu::PercpuBlock,
sync::WaitMap,
syscall::error::{Error, Result, ESRCH},
syscall::error::{Error, Result},
};
use self::process::{Process, ProcessId, ProcessInfo};
use self::{
context::Kstack,
process::{Process, ProcessId, ProcessInfo},
};
pub use self::{
context::{BorrowedHtBuf, Context, ContextId, Status, WaitpidKey},
list::ContextList,
context::{BorrowedHtBuf, Context, Status, WaitpidKey},
switch::switch,
};
@@ -37,9 +47,6 @@ mod arch;
/// Context struct
pub mod context;
/// Context list
mod list;
/// Context switch function
pub mod switch;
@@ -60,32 +67,29 @@ pub mod timeout;
pub use self::switch::switch_finish_hook;
/// Limit on number of contexts
pub const CONTEXT_MAX_CONTEXTS: usize = (isize::max_value() as usize) - 1;
/// Maximum context files
pub const CONTEXT_MAX_FILES: usize = 65_536;
/// Contexts list
static CONTEXTS: RwLock<ContextList> = RwLock::new(ContextList::new());
pub use self::arch::empty_cr3;
static KMAIN_PROCESS: Once<Arc<RwLock<Process>>> = Once::new();
// Set of weak references to all contexts available for scheduling. The only strong references are
// the context file descriptors.
static CONTEXTS: RwLock<BTreeSet<ContextRef>> = RwLock::new(BTreeSet::new());
pub fn init() {
let mut contexts = contexts_mut();
let id = ContextId::from(crate::cpu_id().get() as usize + 1);
let pid = ProcessId::new(0);
let process = Arc::new(RwLock::new(Process {
info: ProcessInfo::default(),
waitpid: Arc::new(WaitMap::new()),
threads: Vec::new(),
}));
let process = KMAIN_PROCESS.call_once(|| {
Arc::new(RwLock::new(Process {
info: ProcessInfo::default(),
waitpid: Arc::new(WaitMap::new()),
threads: Vec::new(),
}))
});
let context_lock = contexts
.insert_context_raw(id, pid, process)
.expect("could not initialize first context");
let mut context = context_lock.write();
let mut context =
Context::new(pid, Arc::clone(process)).expect("failed to create kmain context");
context.sched_affinity = LogicalCpuSet::empty();
context.sched_affinity.atomic_set(crate::cpu_id());
context.name = Cow::Borrowed("kmain");
@@ -96,33 +100,126 @@ pub fn init() {
context.running = true;
context.cpu_id = Some(crate::cpu_id());
let context_lock = Arc::new(RwSpinlock::new(context));
CONTEXTS
.write()
.insert(ContextRef(Arc::downgrade(&context_lock)));
unsafe {
let percpu = PercpuBlock::current();
percpu.switch_internals.set_current_cid(context.cid);
percpu.switch_internals.set_idle_id(context.cid);
percpu
.switch_internals
.set_current_context(Arc::clone(&context_lock));
percpu.switch_internals.set_idle_context(context_lock);
}
}
/// Get the global schemes list, const
pub fn contexts() -> RwLockReadGuard<'static, ContextList> {
pub fn contexts() -> RwLockReadGuard<'static, BTreeSet<ContextRef>> {
CONTEXTS.read()
}
/// Get the global schemes list, mutable
pub fn contexts_mut() -> RwLockWriteGuard<'static, ContextList> {
pub fn contexts_mut() -> RwLockWriteGuard<'static, BTreeSet<ContextRef>> {
CONTEXTS.write()
}
pub fn current_cid() -> ContextId {
PercpuBlock::current().switch_internals.current_cid()
pub fn current() -> Arc<RwSpinlock<Context>> {
PercpuBlock::current()
.switch_internals
.with_context(|context| Arc::clone(context))
}
pub fn current_pid() -> Result<ProcessId> {
Ok(current()?.read().pid)
pub fn is_current(context: &Arc<RwSpinlock<Context>>) -> bool {
PercpuBlock::current()
.switch_internals
.with_context(|current| Arc::ptr_eq(context, current))
}
pub fn current() -> Result<Arc<RwSpinlock<Context>>> {
contexts()
.current()
.ok_or(Error::new(ESRCH))
.map(Arc::clone)
pub fn current_pid() -> Result<ProcessId> {
Ok(current().read().pid)
}
pub struct ContextRef(pub Weak<RwSpinlock<Context>>);
impl Ord for ContextRef {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
Ord::cmp(&self.0.as_ptr(), &other.0.as_ptr())
}
}
impl PartialOrd for ContextRef {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(Ord::cmp(self, other))
}
}
impl PartialEq for ContextRef {
fn eq(&self, other: &Self) -> bool {
Ord::cmp(self, other) == core::cmp::Ordering::Equal
}
}
impl Eq for ContextRef {}
/// Spawn a context from a function.
pub fn spawn(
userspace_allowed: bool,
process: Arc<RwLock<Process>>,
func: extern "C" fn(),
) -> Result<Arc<RwSpinlock<Context>>> {
let stack = Kstack::new()?;
let context_lock = Arc::try_new(RwSpinlock::new(Context::new(
process.read().pid,
Arc::clone(&process),
)?))
.map_err(|_| Error::new(ENOMEM))?;
CONTEXTS
.write()
.insert(ContextRef(Arc::downgrade(&context_lock)));
process.write().threads.push(Arc::downgrade(&context_lock));
{
let mut context = context_lock.write();
let _ = context.set_addr_space(Some(AddrSpaceWrapper::new()?));
let mut stack_top = stack.initial_top();
const INT_REGS_SIZE: usize = core::mem::size_of::<crate::interrupt::InterruptStack>();
if userspace_allowed {
unsafe {
// Zero-initialize InterruptStack registers.
stack_top = stack_top.sub(INT_REGS_SIZE);
stack_top.write_bytes(0_u8, INT_REGS_SIZE);
(&mut *stack_top.cast::<InterruptStack>()).init();
}
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
unsafe {
if userspace_allowed {
stack_top = stack_top.sub(core::mem::size_of::<usize>());
stack_top
.cast::<usize>()
.write(crate::interrupt::syscall::enter_usermode as usize);
}
stack_top = stack_top.sub(core::mem::size_of::<usize>());
stack_top.cast::<usize>().write(func as usize);
}
#[cfg(target_arch = "aarch64")]
unsafe {
context
.arch
.set_lr(crate::interrupt::syscall::enter_usermode as usize);
context.arch.set_x28(func as usize);
context.arch.set_context_handle();
}
context.arch.set_stack(stack_top as usize);
context.kstack = Some(stack);
context.userspace = userspace_allowed;
}
Ok(context_lock)
}
+1 -1
View File
@@ -91,7 +91,7 @@ pub fn ancestors(
}
pub fn current() -> Result<Arc<RwLock<Process>>> {
let pid = context::current()?.read().pid;
let pid = context::current().read().pid;
Ok(Arc::clone(
PROCESSES.read().get(&pid).ok_or(Error::new(ESRCH))?,
))
+2 -2
View File
@@ -6,7 +6,7 @@ use crate::{
};
pub fn signal_handler() {
let context_lock = context::current().expect("running signal handler outside of context");
let context_lock = context::current();
let mut context = context_lock.write();
let context = &mut *context;
@@ -71,7 +71,7 @@ pub fn signal_handler() {
);
}
pub fn excp_handler(_signal: usize) {
let current = context::current().expect("CPU exception but not inside of context!");
let current = context::current();
let context = current.write();
let Some(_eh) = context.sig.as_ref().and_then(|s| s.excp_handler) else {
+47 -29
View File
@@ -1,7 +1,12 @@
use core::{cell::Cell, mem, ops::Bound, sync::atomic::Ordering};
use core::{
cell::{Cell, RefCell},
mem,
ops::Bound,
sync::atomic::Ordering,
};
use alloc::sync::Arc;
use spinning_top::guard::ArcRwSpinlockWriteGuard;
use spinning_top::{guard::ArcRwSpinlockWriteGuard, RwSpinlock};
use syscall::PtraceFlags;
use crate::{
@@ -12,7 +17,7 @@ use crate::{
ptrace, time,
};
use super::ContextId;
use super::ContextRef;
enum UpdateResult {
CanSwitch,
@@ -116,31 +121,32 @@ pub fn switch() -> SwitchResult {
let contexts = contexts();
// Lock previous context
let prev_context_lock = contexts
.current()
.expect("context::switch: not inside of context");
let prev_context_lock = crate::context::current();
let prev_context_guard = prev_context_lock.write_arc();
let idle_id = percpu.switch_internals.idle_id();
let idle_context = percpu.switch_internals.idle_context();
let mut skip_idle = true;
// Locate next context
for (cid, next_context_lock) in contexts
for next_context_lock in contexts
// Include all contexts with IDs greater than the current...
.range((Bound::Excluded(prev_context_guard.cid), Bound::Unbounded))
.range((
Bound::Excluded(ContextRef(Arc::downgrade(&prev_context_lock))),
Bound::Unbounded,
))
.chain(
contexts
// ... and all contexts with IDs less than the current...
.range((Bound::Unbounded, Bound::Excluded(prev_context_guard.cid))),
)
.chain(
contexts
// ... and finally the idle ID
.range((Bound::Included(idle_id), Bound::Included(idle_id))),
.range((
Bound::Unbounded,
Bound::Excluded(ContextRef(Arc::downgrade(&prev_context_lock))),
)),
)
.filter_map(|r| r.0.upgrade())
.chain(Some(Arc::clone(&idle_context)))
// ... but not the current context, which is already locked
{
if cid == &idle_id && skip_idle {
if Arc::ptr_eq(&next_context_lock, &idle_context) && skip_idle {
// Skip idle process the first time it shows up
skip_idle = false;
continue;
@@ -178,7 +184,11 @@ pub fn switch() -> SwitchResult {
next_context.switch_time = switch_time;
let percpu = PercpuBlock::current();
percpu.switch_internals.current_cid.set(next_context.cid);
unsafe {
percpu.switch_internals.set_current_context(Arc::clone(
ArcRwSpinlockWriteGuard::rwlock(&next_context_guard),
));
}
// FIXME set th switch result in arch::switch_to instead
let prev_context =
@@ -245,25 +255,33 @@ pub struct ContextSwitchPercpu {
switch_result: Cell<Option<SwitchResultInner>>,
pit_ticks: Cell<usize>,
/// Unique context ID of the currently running context.
current_cid: Cell<ContextId>,
current_ctxt: RefCell<Option<Arc<RwSpinlock<Context>>>>,
// The ID of the idle process
idle_id: Cell<ContextId>,
// The idle process
idle_ctxt: RefCell<Option<Arc<RwSpinlock<Context>>>>,
pub(crate) being_sigkilled: Cell<bool>,
}
impl ContextSwitchPercpu {
pub fn current_cid(&self) -> ContextId {
self.current_cid.get()
pub fn with_context<T>(&self, f: impl FnOnce(&Arc<RwSpinlock<Context>>) -> T) -> T {
f(&*self
.current_ctxt
.borrow()
.as_ref()
.expect("not inside of context"))
}
pub unsafe fn set_current_cid(&self, new: ContextId) {
self.current_cid.set(new)
pub unsafe fn set_current_context(&self, new: Arc<RwSpinlock<Context>>) {
*self.current_ctxt.borrow_mut() = Some(new);
}
pub fn idle_id(&self) -> ContextId {
self.idle_id.get()
pub unsafe fn set_idle_context(&self, new: Arc<RwSpinlock<Context>>) {
*self.idle_ctxt.borrow_mut() = Some(new);
}
pub unsafe fn set_idle_id(&self, new: ContextId) {
self.idle_id.set(new)
pub fn idle_context(&self) -> Arc<RwSpinlock<Context>> {
Arc::clone(
self.idle_ctxt
.borrow()
.as_ref()
.expect("no idle context present"),
)
}
}