diff --git a/services/libs/jinux-std/src/process/clone.rs b/services/libs/jinux-std/src/process/clone.rs index ca62feb63..e0d9b4dbd 100644 --- a/services/libs/jinux-std/src/process/clone.rs +++ b/services/libs/jinux-std/src/process/clone.rs @@ -340,7 +340,7 @@ fn clone_vm(parent_process_vm: &ProcessVm, clone_flags: CloneFlags) -> Result::fork_from(parent_process_vm.root_vmar())?; let user_heap = parent_process_vm.user_heap().clone(); Ok(ProcessVm::new(user_heap, root_vmar)) } diff --git a/services/libs/jinux-std/src/process/process/builder.rs b/services/libs/jinux-std/src/process/process/builder.rs index 3d48cb2dd..def036130 100644 --- a/services/libs/jinux-std/src/process/process/builder.rs +++ b/services/libs/jinux-std/src/process/process/builder.rs @@ -134,9 +134,7 @@ impl<'a> ProcessBuilder<'a> { sig_dispositions, } = self; - let process_vm = process_vm - .or_else(|| Some(ProcessVm::alloc().unwrap())) - .unwrap(); + let process_vm = process_vm.or_else(|| Some(ProcessVm::alloc())).unwrap(); let process_group_ref = process_group .as_ref() diff --git a/services/libs/jinux-std/src/process/process_vm/mod.rs b/services/libs/jinux-std/src/process/process_vm/mod.rs index dd07d1fa2..8a9084146 100644 --- a/services/libs/jinux-std/src/process/process_vm/mod.rs +++ b/services/libs/jinux-std/src/process/process_vm/mod.rs @@ -6,7 +6,6 @@ pub mod user_heap; -use crate::prelude::*; use jinux_rights::Full; use user_heap::UserHeap; @@ -55,14 +54,14 @@ impl Clone for ProcessVm { } impl ProcessVm { - pub fn alloc() -> Result { - let root_vmar = Vmar::::new_root()?; + pub fn alloc() -> Self { + let root_vmar = Vmar::::new_root(); let user_heap = UserHeap::new(); user_heap.init(&root_vmar); - Ok(ProcessVm { + ProcessVm { user_heap, root_vmar, - }) + } } pub fn new(user_heap: UserHeap, root_vmar: Vmar) -> Self { diff --git a/services/libs/jinux-std/src/thread/kernel_thread.rs b/services/libs/jinux-std/src/thread/kernel_thread.rs index 378768b71..71ddde4b5 100644 --- a/services/libs/jinux-std/src/thread/kernel_thread.rs +++ b/services/libs/jinux-std/src/thread/kernel_thread.rs @@ -44,8 +44,7 @@ impl KernelThreadExt for Thread { }; let tid = allocate_tid(); let thread = Arc::new_cyclic(|thread_ref| { - let weal_thread = thread_ref.clone(); - let task = Task::new(thread_fn, weal_thread, None).unwrap(); + let task = Task::new(thread_fn, thread_ref.clone(), None).unwrap(); let status = ThreadStatus::Init; let kernel_thread = KernelThread; Thread::new(tid, task, kernel_thread, status) diff --git a/services/libs/jinux-std/src/vm/vmar/dyn_cap.rs b/services/libs/jinux-std/src/vm/vmar/dyn_cap.rs index 17e277f40..9c3904459 100644 --- a/services/libs/jinux-std/src/vm/vmar/dyn_cap.rs +++ b/services/libs/jinux-std/src/vm/vmar/dyn_cap.rs @@ -12,11 +12,10 @@ use super::{ impl Vmar { /// Creates a root VMAR. - pub fn new_root() -> Result { - let inner = Arc::new(Vmar_::new_root()?); + pub fn new_root() -> Self { + let inner = Vmar_::new_root(); let rights = Rights::all(); - let new_self = Self(inner, rights); - Ok(new_self) + Self(inner, rights) } /// Maps the given VMO into the VMAR through a set of VMAR mapping options. @@ -143,10 +142,16 @@ impl Vmar { Ok(Vmar(self.0.clone(), self.1)) } - /// Given a map size, returns the possible map address without doing actual allocation. - pub fn hint_map_addr(&self, map_size: usize) -> Result { - self.check_rights(Rights::READ)?; - self.0.hint_map_addr(map_size) + /// Creates a new root VMAR whose content is inherited from another + /// using copy-on-write (COW) technique. + /// + /// # Access rights + /// + /// The method requires the Read right. + pub fn fork_from(vmar: &Vmar) -> Result { + vmar.check_rights(Rights::READ)?; + let vmar_ = vmar.0.new_cow_root()?; + Ok(Vmar(vmar_, Rights::all())) } } diff --git a/services/libs/jinux-std/src/vm/vmar/mod.rs b/services/libs/jinux-std/src/vm/vmar/mod.rs index fd7f5f71b..dfb02cd81 100644 --- a/services/libs/jinux-std/src/vm/vmar/mod.rs +++ b/services/libs/jinux-std/src/vm/vmar/mod.rs @@ -14,7 +14,7 @@ use alloc::sync::Weak; use alloc::vec::Vec; use core::ops::Range; use jinux_frame::vm::VmSpace; -use jinux_rights::{Dup, Exec, Full, Read, Rights, Signal, TRightSet, TRights, Write}; +use jinux_rights::Rights; use self::vm_mapping::VmMapping; @@ -112,14 +112,47 @@ struct VmarInner { free_regions: BTreeMap, } +impl VmarInner { + const fn new() -> Self { + Self { + is_destroyed: false, + child_vmar_s: BTreeMap::new(), + vm_mappings: BTreeMap::new(), + free_regions: BTreeMap::new(), + } + } +} + // FIXME: How to set the correct root vmar range? // We should not include addr 0 here(is this right?), since the 0 addr means the null pointer. // We should include addr 0x0040_0000, since non-pie executables typically are put on 0x0040_0000. -pub const ROOT_VMAR_LOWEST_ADDR: Vaddr = 0x0010_0000; -pub const ROOT_VMAR_HIGHEST_ADDR: Vaddr = 0x1000_0000_0000; +const ROOT_VMAR_LOWEST_ADDR: Vaddr = 0x0010_0000; +const ROOT_VMAR_HIGHEST_ADDR: Vaddr = 0x1000_0000_0000; impl Vmar_ { - pub fn new_root() -> Result { + fn new( + inner: VmarInner, + vm_space: VmSpace, + base: usize, + size: usize, + parent: Option<&Arc>, + ) -> Arc { + let parent = if let Some(parent) = parent { + Arc::downgrade(parent) + } else { + Weak::new() + }; + + Arc::new(Vmar_ { + inner: Mutex::new(inner), + base, + size, + vm_space, + parent, + }) + } + + pub fn new_root() -> Arc { let mut free_regions = BTreeMap::new(); let root_region = FreeRegion::new(ROOT_VMAR_LOWEST_ADDR..ROOT_VMAR_HIGHEST_ADDR); free_regions.insert(root_region.start(), root_region); @@ -129,14 +162,7 @@ impl Vmar_ { vm_mappings: BTreeMap::new(), free_regions, }; - let vmar_ = Vmar_ { - inner: Mutex::new(vmar_inner), - vm_space: VmSpace::new(), - base: 0, - size: ROOT_VMAR_HIGHEST_ADDR, - parent: Weak::new(), - }; - Ok(vmar_) + Vmar_::new(vmar_inner, VmSpace::new(), 0, ROOT_VMAR_HIGHEST_ADDR, None) } fn is_root_vmar(&self) -> bool { @@ -445,13 +471,13 @@ impl Vmar_ { vm_mappings: BTreeMap::new(), free_regions: child_regions, }; - let child_vmar_ = Arc::new(Vmar_ { - inner: Mutex::new(child_vmar_inner), - base: child_vmar_offset, - size: child_vmar_size, - vm_space: self.vm_space.clone(), - parent: Arc::downgrade(self), - }); + let child_vmar_ = Vmar_::new( + child_vmar_inner, + self.vm_space.clone(), + child_vmar_offset, + child_vmar_size, + Some(self), + ); self.inner .lock() .child_vmar_s @@ -591,16 +617,6 @@ impl Vmar_ { } } - fn hint_map_addr(&self, size: usize) -> Result { - let inner = self.inner.lock(); - for (free_region_base, free_region) in &inner.free_regions { - if free_region.size() >= size { - return Ok(*free_region_base); - } - } - return_errno_with_message!(Errno::ENOMEM, "cannot find a suitale free region"); - } - fn trim_existing_mappings(&self, trim_range: Range) -> Result<()> { let mut inner = self.inner.lock(); let mut mappings_to_remove = BTreeSet::new(); @@ -622,35 +638,28 @@ impl Vmar_ { Ok(()) } - /// fork vmar for child process - pub fn fork_vmar_(&self, parent: Weak) -> Result> { - // create an empty vmar at first - let is_destroyed = false; - let child_vmar_s = BTreeMap::new(); - let mapped_vmos = BTreeMap::new(); - let free_regions = BTreeMap::new(); - let vmar_inner = VmarInner { - is_destroyed, - child_vmar_s, - vm_mappings: mapped_vmos, - free_regions, - }; - // If this is a root vmar, we create a new vmspace - // Otherwise, we clone the vm space from parent. - let vm_space = if let Some(parent) = parent.upgrade() { - parent.vm_space().clone() - } else { - VmSpace::new() - }; - let vmar_ = Vmar_ { - inner: Mutex::new(vmar_inner), - base: self.base, - size: self.size, - vm_space, - parent, + pub(super) fn new_cow_root(self: &Arc) -> Result> { + if self.parent.upgrade().is_some() { + return_errno_with_message!(Errno::EINVAL, "can only dup cow vmar for root vmar"); + } + + self.new_cow(None) + } + + /// Create a new vmar by creating cow child for all mapped vmos + fn new_cow(&self, parent: Option<&Arc>) -> Result> { + let new_vmar_ = { + let vmar_inner = VmarInner::new(); + // If this is a root vmar, we create a new vmspace, + // Otherwise, we clone the vm space from parent. + let vm_space = if let Some(parent) = parent { + parent.vm_space().clone() + } else { + VmSpace::new() + }; + Vmar_::new(vmar_inner, vm_space, self.base, self.size, parent) }; - let new_vmar_ = Arc::new(vmar_); let inner = self.inner.lock(); // clone free regions for (free_region_base, free_region) in &inner.free_regions { @@ -663,30 +672,28 @@ impl Vmar_ { // clone child vmars for (child_vmar_base, child_vmar_) in &inner.child_vmar_s { - let parent_of_forked_child = Arc::downgrade(&new_vmar_); - let forked_child_vmar = child_vmar_.fork_vmar_(parent_of_forked_child)?; + let new_child_vmar = child_vmar_.new_cow(Some(&new_vmar_))?; new_vmar_ .inner .lock() .child_vmar_s - .insert(*child_vmar_base, forked_child_vmar); + .insert(*child_vmar_base, new_child_vmar); } // clone vm mappings for (vm_mapping_base, vm_mapping) in &inner.vm_mappings { - let parent_of_forked_mapping = Arc::downgrade(&new_vmar_); - let forked_mapping = Arc::new(vm_mapping.fork_mapping(parent_of_forked_mapping)?); + let new_mapping = Arc::new(vm_mapping.new_cow(&new_vmar_)?); new_vmar_ .inner .lock() .vm_mappings - .insert(*vm_mapping_base, forked_mapping); + .insert(*vm_mapping_base, new_mapping); } Ok(new_vmar_) } /// get mapped vmo at given offset - pub fn get_vm_mapping(&self, offset: Vaddr) -> Result> { + fn get_vm_mapping(&self, offset: Vaddr) -> Result> { for (vm_mapping_base, vm_mapping) in &self.inner.lock().vm_mappings { if *vm_mapping_base <= offset && offset < *vm_mapping_base + vm_mapping.map_size() { return Ok(vm_mapping.clone()); @@ -709,17 +716,6 @@ impl Vmar { self.0.size } - /// Fork a vmar for child process - pub fn fork_vmar(&self) -> Result> { - let rights = Rights::all(); - self.check_rights(rights)?; - let vmar_ = self.0.fork_vmar_(Weak::new())?; - Ok(Vmar( - vmar_, - TRightSet(::new()), - )) - } - /// get a mapped vmo pub fn get_vm_mapping(&self, offset: Vaddr) -> Result> { let rights = Rights::all(); diff --git a/services/libs/jinux-std/src/vm/vmar/options.rs b/services/libs/jinux-std/src/vm/vmar/options.rs index c478ec69c..15fc4b70d 100644 --- a/services/libs/jinux-std/src/vm/vmar/options.rs +++ b/services/libs/jinux-std/src/vm/vmar/options.rs @@ -144,13 +144,13 @@ mod test { #[test] fn root_vmar() { - let vmar = Vmar::::new_root().unwrap(); + let vmar = Vmar::::new_root(); assert!(vmar.size() == ROOT_VMAR_HIGHEST_ADDR); } #[test] fn child_vmar() { - let root_vmar = Vmar::::new_root().unwrap(); + let root_vmar = Vmar::::new_root(); let root_vmar_dup = root_vmar.dup().unwrap(); let child_vmar = VmarChildOptions::new(root_vmar_dup, 10 * PAGE_SIZE) .alloc() @@ -169,7 +169,7 @@ mod test { #[test] fn map_vmo() { - let root_vmar = Vmar::::new_root().unwrap(); + let root_vmar = Vmar::::new_root(); let vmo = VmoOptions::::new(PAGE_SIZE).alloc().unwrap().to_dyn(); let perms = VmPerms::READ | VmPerms::WRITE; let map_offset = 0x1000_0000; @@ -196,7 +196,7 @@ mod test { #[test] fn handle_page_fault() { const OFFSET: usize = 0x1000_0000; - let root_vmar = Vmar::::new_root().unwrap(); + let root_vmar = Vmar::::new_root(); // the page is not mapped by a vmo assert!(root_vmar.handle_page_fault(OFFSET, true, true).is_err()); // the page is mapped READ diff --git a/services/libs/jinux-std/src/vm/vmar/static_cap.rs b/services/libs/jinux-std/src/vm/vmar/static_cap.rs index 34fe8bd84..76b900e1d 100644 --- a/services/libs/jinux-std/src/vm/vmar/static_cap.rs +++ b/services/libs/jinux-std/src/vm/vmar/static_cap.rs @@ -2,7 +2,7 @@ use core::ops::Range; use crate::prelude::*; use jinux_frame::vm::VmIo; -use jinux_rights::{Dup, Read, Rights, TRightSet, TRights}; +use jinux_rights::{Dup, Rights, TRightSet, TRights}; use jinux_rights_proc::require; use crate::vm::{page_fault_handler::PageFaultHandler, vmo::Vmo}; @@ -17,11 +17,10 @@ impl Vmar> { /// # Access rights /// /// A root VMAR is initially given full access rights. - pub fn new_root() -> Result { - let inner = Arc::new(Vmar_::new_root()?); + pub fn new_root() -> Self { + let inner = Vmar_::new_root(); let rights = R::new(); - let new_self = Self(inner, TRightSet(rights)); - Ok(new_self) + Self(inner, TRightSet(rights)) } /// Maps the given VMO into the VMAR through a set of VMAR mapping options. @@ -150,10 +149,16 @@ impl Vmar> { Ok(Vmar(self.0.clone(), self.1)) } - /// Given a map size, returns the possible map address without doing actual allocation. - #[require(R > Read)] - pub fn hint_map_addr(&self, map_size: usize) -> Result { - self.0.hint_map_addr(map_size) + /// Creates a new root VMAR whose content is inherited from another + /// using copy-on-write (COW) technique. + /// + /// # Access rights + /// + /// The method requires the Read right. + pub fn fork_from(vmar: &Vmar) -> Result { + vmar.check_rights(Rights::READ)?; + let vmar_ = vmar.0.new_cow_root()?; + Ok(Vmar(vmar_, TRightSet(R::new()))) } /// Strict the access rights. diff --git a/services/libs/jinux-std/src/vm/vmar/vm_mapping.rs b/services/libs/jinux-std/src/vm/vmar/vm_mapping.rs index 87e179473..53b603a64 100644 --- a/services/libs/jinux-std/src/vm/vmar/vm_mapping.rs +++ b/services/libs/jinux-std/src/vm/vmar/vm_mapping.rs @@ -101,6 +101,7 @@ impl VmMapping { mapped_pages: BTreeSet::new(), page_perms, }; + Ok(Self { inner: Mutex::new(vm_mapping_inner), parent: Arc::downgrade(&parent_vmar), @@ -233,18 +234,30 @@ impl VmMapping { self.inner.lock().protect(vm_space, perms, range) } - pub(super) fn fork_mapping(&self, new_parent: Weak) -> Result { + pub(super) fn new_cow(&self, new_parent: &Arc) -> Result { let VmMapping { inner, vmo, .. } = self; + let child_vmo = { let parent_vmo = vmo.dup().unwrap(); let vmo_size = parent_vmo.size(); VmoChildOptions::new_cow(parent_vmo, 0..vmo_size).alloc()? }; - let inner = self.inner.lock().fork_mapping(child_vmo.size())?; + let new_inner = { + let inner = self.inner.lock(); + VmMappingInner { + vmo_offset: inner.vmo_offset, + map_size: inner.map_size, + map_to_addr: inner.map_to_addr, + is_destroyed: inner.is_destroyed, + mapped_pages: BTreeSet::new(), + page_perms: inner.page_perms.clone(), + } + }; + Ok(VmMapping { - inner: Mutex::new(inner), - parent: new_parent, + inner: Mutex::new(new_inner), + parent: Arc::downgrade(new_parent), vmo: child_vmo, }) } @@ -412,21 +425,6 @@ impl VmMappingInner { Ok(()) } - fn fork_mapping(&self, vmo_size: usize) -> Result { - debug!( - "fork vmo, parent size = 0x{:x}, map_to_addr = 0x{:x}", - vmo_size, self.map_to_addr - ); - Ok(VmMappingInner { - is_destroyed: self.is_destroyed, - mapped_pages: BTreeSet::new(), - page_perms: self.page_perms.clone(), - vmo_offset: self.vmo_offset, - map_size: self.map_size, - map_to_addr: self.map_to_addr, - }) - } - /// trim the mapping from left to a new address. fn trim_left(&mut self, vm_space: &VmSpace, vaddr: Vaddr) -> Result { trace!(