WIP: Rudimentary MAP_SHARED fmap implementation.

This commit is contained in:
4lDO2
2023-06-30 18:24:12 +02:00
parent afa61601c4
commit ad78dcc5a1
7 changed files with 115 additions and 19 deletions
+29 -6
View File
@@ -1,3 +1,4 @@
use alloc::sync::Weak;
use alloc::{
boxed::Box,
collections::VecDeque,
@@ -28,11 +29,13 @@ use crate::syscall::flag::{SIG_DFL, SigActionFlags};
/// Unique identifier for a context (i.e. `pid`).
use ::core::sync::atomic::AtomicUsize;
use super::memory::FmapCtxt;
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
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug)]
pub enum Status {
Runnable,
@@ -49,9 +52,19 @@ pub enum Status {
Stopped(usize),
Exited(usize),
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
impl Status {
pub fn is_runnable(&self) -> bool {
matches!(self, Self::Runnable)
}
pub fn is_soft_blocked(&self) -> bool {
matches!(self, Self::Blocked)
}
}
#[derive(Clone, Debug)]
pub enum HardBlockedReason {
AwaitingMmap,
AwaitingMmap { ctxt: Arc<FmapCtxt>, finished: Option<Frame> },
// TODO: PageFaultOom?
// TODO: NotYetStarted/ManuallyBlocked (when new contexts are created)
// TODO: ptrace_stop?
@@ -174,7 +187,7 @@ impl ContextSnapshot {
ens: context.ens,
sigmask: context.sigmask,
umask: context.umask,
status: context.status,
status: context.status.clone(),
status_reason: context.status_reason,
running: context.running,
cpu_id: context.cpu_id,
@@ -329,7 +342,7 @@ impl Context {
/// Block the context, and return true if it was runnable before being blocked
pub fn block(&mut self, reason: &'static str) -> bool {
if self.status == Status::Runnable {
if self.status.is_runnable() {
self.status = Status::Blocked;
self.status_reason = reason;
true
@@ -338,9 +351,19 @@ impl Context {
}
}
pub fn hard_block(&mut self, reason: HardBlockedReason) -> bool {
if self.status.is_runnable() {
self.status = Status::HardBlocked { reason };
true
} else {
false
}
}
/// Unblock context, and return true if it was blocked before being marked runnable
pub fn unblock(&mut self) -> bool {
if self.status == Status::Blocked {
if self.status.is_soft_blocked() {
self.status = Status::Runnable;
self.status_reason = "";
+34 -4
View File
@@ -15,7 +15,9 @@ use crate::arch::paging::PAGE_SIZE;
use crate::memory::{Enomem, Frame, get_page_info, PageInfo};
use crate::paging::mapper::{Flusher, InactiveFlusher, PageFlushAll};
use crate::paging::{KernelMapper, Page, PageFlags, PageMapper, RmmA, TableKind, VirtualAddress};
use crate::scheme;
use super::context::HardBlockedReason;
use super::file::FileDescription;
pub const MMAP_MIN_DEFAULT: usize = PAGE_SIZE;
@@ -1129,13 +1131,13 @@ pub unsafe fn copy_frame_to_frame_directly(dst: Frame, src: Frame) {
}
pub fn try_correcting_page_tables(faulting_page: Page, access: AccessMode) -> Result<(), PfError> {
let Ok(addr_space) = AddrSpace::current() else {
let Ok(addr_space_lock) = AddrSpace::current() else {
log::debug!("User page fault without address space being set.");
return Err(PfError::Segv);
};
let mut addr_space = addr_space.write();
let addr_space = &mut *addr_space;
let mut addr_space_guard = addr_space_lock.write();
let mut addr_space = &mut *addr_space_guard;
let Some((grant_base, grant_info)) = addr_space.grants.contains(faulting_page) else {
log::debug!("Lacks grant");
@@ -1232,7 +1234,35 @@ pub fn try_correcting_page_tables(faulting_page: Page, access: AccessMode) -> Re
map_zeroed(&mut guard.table.utable, src_page, grant_flags, access == AccessMode::Write)?
}
}
Provider::FmapBorrowed { ref fmap } => todo!(),
Provider::FmapBorrowed { ref fmap } => {
let ctxt = Arc::clone(fmap);
let flags = map_flags(grant_info.flags());
drop(addr_space);
let (scheme_id, scheme_number) = match ctxt.file_ref.description.read() {
ref desc => (desc.scheme, desc.number),
};
let user_inner = scheme::schemes()
.get(scheme_id).and_then(|s| s.as_user_inner().transpose().ok().flatten())
.ok_or(PfError::Segv)?;
let offset = ctxt.file_ref.base_offset as u64 + (pages_from_grant_start * PAGE_SIZE) as u64;
user_inner.request_fmap(scheme_number, offset, 1, flags).unwrap();
let context_lock = super::current().map_err(|_| PfError::NonfatalInternalError)?;
context_lock.write().hard_block(HardBlockedReason::AwaitingMmap { ctxt, finished: None });
unsafe { super::switch(); }
let super::Status::HardBlocked { reason: HardBlockedReason::AwaitingMmap { finished: Some(frame), .. } } = core::mem::replace(&mut context_lock.write().status, super::Status::Runnable) else {
return Err(PfError::NonfatalInternalError);
};
addr_space_guard = addr_space_lock.write();
addr_space = &mut *addr_space_guard;
frame
}
};
if super::context_id().into() == 3 && debug {
+1 -1
View File
@@ -28,7 +28,7 @@ mod arch;
mod arch;
/// Context struct
mod context;
pub mod context;
/// Context list
mod list;
+3 -3
View File
@@ -64,12 +64,12 @@ unsafe fn update_runnable(context: &mut Context, cpu_id: usize) -> bool {
}
// Unblock when there are pending signals
if context.status == Status::Blocked && !context.pending.is_empty() {
if context.status.is_soft_blocked() && !context.pending.is_empty() {
context.unblock();
}
// Wake from sleep
if context.status == Status::Blocked && context.wake.is_some() {
if context.status.is_soft_blocked() && context.wake.is_some() {
let wake = context.wake.expect("context::switch: wake not set");
let current = time::monotonic();
@@ -80,7 +80,7 @@ unsafe fn update_runnable(context: &mut Context, cpu_id: usize) -> bool {
}
// Switch to context if it needs to run
context.status == Status::Runnable
context.status.is_runnable()
}
struct SwitchResult {
+4
View File
@@ -37,6 +37,7 @@ use self::root::RootScheme;
use self::serio::SerioScheme;
use self::sys::SysScheme;
use self::time::TimeScheme;
use self::user::UserInner;
/// When compiled with the "acpi" feature - `acpi:` - allows drivers to read a limited set of ACPI tables.
#[cfg(all(feature = "acpi", any(target_arch = "x86", target_arch = "x86_64")))]
@@ -335,6 +336,9 @@ pub trait KernelScheme: Scheme + Send + Sync + 'static {
fn kfstatvfs(&self, id: usize, buf: UserSliceWo) -> Result<usize> {
Err(Error::new(EBADF))
}
// TODO: This demonstrates why we need to transition away from a dyn trait.
fn as_user_inner(&self) -> Option<Result<Arc<UserInner>>> { None }
}
#[derive(Debug)]
+43 -4
View File
@@ -8,10 +8,12 @@ use core::{mem, usize};
use core::convert::TryFrom;
use spin::{Mutex, RwLock};
use crate::context::{self, Context, BorrowedHtBuf};
use crate::context::context::HardBlockedReason;
use crate::context::{self, Context, BorrowedHtBuf, Status};
use crate::context::file::FileDescription;
use crate::context::memory::{AddrSpace, DANGLING, Grant, GrantFileRef, PageSpan, MmapMode, page_flags, FmapCtxt};
use crate::event;
use crate::memory::Frame;
use crate::paging::{PAGE_SIZE, Page, VirtualAddress};
use crate::scheme::{AtomicSchemeId, SchemeId};
use crate::sync::{WaitQueue, WaitMap};
@@ -34,6 +36,7 @@ pub struct UserInner {
context: Weak<RwLock<Context>>,
todo: WaitQueue<Packet>,
done: WaitMap<u64, Response>,
fmap: Mutex<BTreeMap<u64, Weak<RwLock<Context>>>>,
unmounting: AtomicBool,
}
pub enum Response {
@@ -56,6 +59,7 @@ impl UserInner {
todo: WaitQueue::new(),
done: WaitMap::new(),
unmounting: AtomicBool::new(false),
fmap: Mutex::new(BTreeMap::new()),
}
}
@@ -341,6 +345,21 @@ impl UserInner {
Ok(packets_read * mem::size_of::<Packet>())
}
pub fn request_fmap(&self, id: usize, offset: u64, required_page_count: usize, flags: MapFlags) -> Result<()> {
self.todo.send(Packet {
id: self.next_id(),
pid: context::context_id().into(),
a: KSMSG_MMAP,
b: id,
c: flags.bits(),
d: required_page_count,
uid: offset as u32,
gid: (offset >> 32) as u32,
});
event::trigger(self.root_id, self.handle_id, EVENT_READ);
Ok(())
}
fn handle_packet(&self, packet: &Packet) -> Result<()> {
if packet.id == 0 {
// TODO: Simplify logic by using SKMSG with packet.id being ignored?
@@ -360,15 +379,31 @@ impl UserInner {
self.done.send(packet.id, Response::Fd(desc));
}
SKMSG_PROVIDE_MMAP => {
log::info!("PROVIDE_MAP {:?}", packet);
let offset = u64::from(packet.uid) | (u64::from(packet.gid) << 32);
if offset % PAGE_SIZE as u64 != 0 {
return Err(Error::new(EINVAL));
return dbg!(Err(Error::new(EINVAL)));
}
let page_count = packet.c;
let base_addr = VirtualAddress::new(packet.c);
if base_addr.data() % PAGE_SIZE != 0 {
return dbg!(Err(Error::new(EINVAL)));
}
let page_count = packet.d;
if page_count != 1 { return dbg!(Err(Error::new(EINVAL))); }
let context = self.fmap.lock().remove(&packet.id).ok_or(Error::new(EINVAL))?.upgrade().ok_or(Error::new(ESRCH))?;
let (frame, _) = AddrSpace::current()?.read().table.utable.translate(base_addr).ok_or(Error::new(EFAULT))?;
let mut context = context.write();
match context.status {
Status::HardBlocked { reason: HardBlockedReason::AwaitingMmap { ref mut finished, .. } } => *finished = Some(Frame::containing_address(frame)),
_ => (),
}
todo!("respond to SKMSG_PROVIDE_MMAP")
}
_ => return Err(Error::new(EINVAL)),
}
@@ -752,6 +787,10 @@ impl KernelScheme for UserScheme {
inner.fmap_inner(Arc::clone(addr_space), file, map)
}
fn as_user_inner(&self) -> Option<Result<Arc<UserInner>>> {
Some(self.inner.upgrade().ok_or(Error::new(ENODEV)))
}
}
#[derive(PartialEq)]
+1 -1
View File
@@ -487,7 +487,7 @@ pub fn waitpid(pid: ContextId, status_ptr: Option<UserSliceWo>, flags: WaitFlags
println!("TODO: Hack for rustc - changing ppid of {} from {} to {}", context.id.into(), context.ppid.into(), ppid.into());
context.ppid = ppid;
//return Err(Error::new(ECHILD));
Some(context.status)
Some(context.status.clone())
} else {
None
}