WIP: Rudimentary MAP_SHARED fmap implementation.
This commit is contained in:
+29
-6
@@ -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
@@ -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
@@ -28,7 +28,7 @@ mod arch;
|
||||
mod arch;
|
||||
|
||||
/// Context struct
|
||||
mod context;
|
||||
pub mod context;
|
||||
|
||||
/// Context list
|
||||
mod list;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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)]
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user