فهرست منبع

feat: implement shared memory syscalls and mmap syscall for file mapping

zhuowei shao 7 ماه پیش
والد
کامیت
fe995aa528
10فایلهای تغییر یافته به همراه265 افزوده شده و 59 حذف شده
  1. 10 10
      Cargo.lock
  2. 8 5
      Cargo.toml
  3. 1 0
      src/fs/mod.rs
  4. 97 0
      src/fs/shm.rs
  5. 3 2
      src/io.rs
  6. 1 0
      src/kernel/constants.rs
  7. 127 41
      src/kernel/syscall/mm.rs
  8. 4 1
      src/kernel/task/loader/elf.rs
  9. 4 0
      src/kernel/task/process.rs
  10. 10 0
      src/kernel/vfs/file.rs

+ 10 - 10
Cargo.lock

@@ -25,9 +25,9 @@ version = "0.1.0"
 
 [[package]]
 name = "autocfg"
-version = "1.4.0"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
 
 [[package]]
 name = "bit_field"
@@ -51,9 +51,9 @@ dependencies = [
 
 [[package]]
 name = "cfg-if"
-version = "1.0.0"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
 
 [[package]]
 name = "critical-section"
@@ -392,9 +392,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.101"
+version = "2.0.103"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
+checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -459,18 +459,18 @@ checksum = "2fe21bcc34ca7fe6dd56cc2cb1261ea59d6b93620215aefb5ea6032265527784"
 
 [[package]]
 name = "zerocopy"
-version = "0.8.25"
+version = "0.8.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
+checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
 dependencies = [
  "zerocopy-derive",
 ]
 
 [[package]]
 name = "zerocopy-derive"
-version = "0.8.25"
+version = "0.8.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
+checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
 dependencies = [
  "proc-macro2",
  "quote",

+ 8 - 5
Cargo.toml

@@ -43,23 +43,26 @@ log_trace = ["trace_syscall", "trace_scheduler"]
 log_debug = []
 smp = []
 
+[profile.release]
+debug = true
+
 [profile.dev]
 panic = "abort"
 
 [profile.dev.package.eonix_preempt]
-opt-level = 2
+opt-level = "s"
 
 [profile.dev.package.eonix_runtime]
-opt-level = 0
+opt-level = "s"
 
 [profile.dev.package.eonix_sync]
-opt-level = 2
+opt-level = "s"
 
 [profile.dev.package.intrusive_list]
-opt-level = 2
+opt-level = "s"
 
 [profile.dev.package.eonix_hal]
-opt-level = 0
+opt-level = "s"
 
 [profile.dev.package."*"]
 opt-level = "s"

+ 1 - 0
src/fs/mod.rs

@@ -1,4 +1,5 @@
 pub mod fat32;
 pub mod procfs;
+pub mod shm;
 pub mod tmpfs;
 pub mod ext4;

+ 97 - 0
src/fs/shm.rs

@@ -0,0 +1,97 @@
+use core::sync::atomic::{AtomicU32, Ordering};
+
+use alloc::{collections::btree_map::BTreeMap, sync::Arc};
+use bitflags::bitflags;
+use eonix_sync::{LazyLock, Mutex};
+
+use crate::{
+    fs::tmpfs::{DirectoryInode, FileInode, TmpFs},
+    kernel::{constants::ENOSPC, vfs::inode::Mode},
+    prelude::KResult,
+};
+
+bitflags! {
+    #[derive(Debug, Clone, Copy)]
+    pub struct ShmFlags: u32 {
+        /// Create a new segment. If this flag is not used, then shmget() will
+        /// find the segment associated with key and check to see if the user
+        /// has permission to access the segment.
+        const IPC_CREAT = 0o1000;
+        /// This flag is used with IPC_CREAT to ensure that this call creates
+        /// the segment.  If the segment already exists, the call fails.
+        const IPC_EXCL = 0o2000;
+
+        /// Attach the segment for read-only access.If this flag is not specified,
+        /// the segment is attached for read and write access, and the process
+        /// must have read and write permission for the segment.
+        const SHM_RDONLY = 0o10000;
+        /// round attach address to SHMLBA boundary
+        const SHM_RND = 0o20000;
+        /// Allow the contents of the segment to be executed.
+        const SHM_EXEC = 0o100000;
+    }
+}
+
+pub const IPC_PRIVATE: usize = 0;
+
+pub struct ShmManager {
+    tmpfs: Arc<TmpFs>,
+    root: Arc<DirectoryInode>,
+    areas: BTreeMap<u32, ShmArea>,
+}
+
+pub struct ShmArea {
+    pub area: Arc<FileInode>,
+    pub size: usize,
+}
+
+// A big lock here to protect the shared memory area.
+// Can be improved with finer-grained locking?
+pub static SHM_MANAGER: LazyLock<Mutex<ShmManager>> =
+    LazyLock::new(|| Mutex::new(ShmManager::new()));
+
+impl ShmManager {
+    fn new() -> Self {
+        let (tmpfs, root) = TmpFs::create(false).expect("should create shm_area successfully");
+        Self {
+            tmpfs,
+            root,
+            areas: BTreeMap::new(),
+        }
+    }
+
+    pub fn create_shared_area(&self, size: usize, mode: Mode) -> ShmArea {
+        let ino = self.tmpfs.assign_ino();
+        let vfs = Arc::downgrade(&self.tmpfs);
+        ShmArea {
+            area: FileInode::new(ino, vfs, mode),
+            size,
+        }
+    }
+
+    pub fn get(&self, shmid: u32) -> Option<&ShmArea> {
+        self.areas.get(&shmid)
+    }
+
+    pub fn insert(&mut self, shmid: u32, area: ShmArea) {
+        self.areas.insert(shmid, area);
+    }
+}
+
+pub fn gen_shm_id(key: usize) -> KResult<u32> {
+    const SHM_MAGIC: u32 = 114514000;
+
+    static NEXT_SHMID: AtomicU32 = AtomicU32::new(0);
+
+    if key == IPC_PRIVATE {
+        let shmid = NEXT_SHMID.fetch_add(1, Ordering::Relaxed);
+
+        if shmid < SHM_MAGIC {
+            return Err(ENOSPC);
+        } else {
+            return Ok(shmid);
+        }
+    }
+
+    (key as u32).checked_add(SHM_MAGIC).ok_or(ENOSPC)
+}

+ 3 - 2
src/io.rs

@@ -3,6 +3,7 @@ use crate::prelude::*;
 use core::{cmp, mem::MaybeUninit};
 
 #[must_use]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub enum FillResult {
     Done(usize),
     Partial(usize),
@@ -63,7 +64,7 @@ pub trait StreamRead {
     fn read_till_end(
         &mut self,
         buffer: &mut [u8],
-        func: impl Fn(&mut [u8]) -> KResult<()>,
+        func: impl FnMut(&mut [u8]) -> KResult<()>,
     ) -> KResult<usize>;
 
     fn ignore_all(&mut self) -> KResult<usize>;
@@ -76,7 +77,7 @@ where
     fn read_till_end(
         &mut self,
         buffer: &mut [u8],
-        func: impl Fn(&mut [u8]) -> KResult<()>,
+        mut func: impl FnMut(&mut [u8]) -> KResult<()>,
     ) -> KResult<usize> {
         let mut total = 0;
         while let Some(data) = self.poll_data(buffer)? {

+ 1 - 0
src/kernel/constants.rs

@@ -35,6 +35,7 @@ pub const ENOTDIR: u32 = 20;
 pub const EISDIR: u32 = 21;
 pub const EINVAL: u32 = 22;
 pub const ENOTTY: u32 = 25;
+pub const ENOSPC: u32 = 28;
 pub const ESPIPE: u32 = 29;
 // pub const EROFS: u32 = 30;
 pub const EPIPE: u32 = 32;

+ 127 - 41
src/kernel/syscall/mm.rs

@@ -1,6 +1,9 @@
 use super::FromSyscallArg;
-use crate::kernel::constants::{EINVAL, ENOMEM};
+use crate::fs::shm::{gen_shm_id, ShmFlags, IPC_PRIVATE, SHM_MANAGER};
+use crate::kernel::constants::{EBADF, EEXIST, EINVAL, ENOENT, ENOMEM};
+use crate::kernel::mem::FileMapping;
 use crate::kernel::task::Thread;
+use crate::kernel::vfs::filearray::FD;
 use crate::{
     kernel::{
         constants::{UserMmapFlags, UserMmapProtocol},
@@ -26,8 +29,15 @@ impl FromSyscallArg for UserMmapFlags {
     }
 }
 
+impl FromSyscallArg for ShmFlags {
+    fn from_arg(value: usize) -> Self {
+        ShmFlags::from_bits_truncate(value as u32)
+    }
+}
+
 /// Check whether we are doing an implemented function.
 /// If `condition` is false, return `Err(err)`.
+#[allow(unused)]
 fn check_impl(condition: bool, err: u32) -> KResult<()> {
     if !condition {
         Err(err)
@@ -42,60 +52,57 @@ fn do_mmap2(
     len: usize,
     prot: UserMmapProtocol,
     flags: UserMmapFlags,
-    fd: u32,
+    fd: FD,
     pgoffset: usize,
 ) -> KResult<usize> {
     let addr = VAddr::from(addr);
-    if !addr.is_page_aligned() || len == 0 {
+    if !addr.is_page_aligned() || pgoffset % PAGE_SIZE != 0 || len == 0 {
         return Err(EINVAL);
     }
 
     let len = len.align_up(PAGE_SIZE);
-    check_impl(flags.contains(UserMmapFlags::MAP_ANONYMOUS), ENOMEM)?;
-    check_impl(flags.contains(UserMmapFlags::MAP_PRIVATE), EINVAL)?;
-    if fd != u32::MAX || pgoffset != 0 {
-        return Err(EINVAL);
-    }
-
     let mm_list = &thread.process.mm_list;
+    let is_shared = flags.contains(UserMmapFlags::MAP_SHARED);
+
+    let mapping = if flags.contains(UserMmapFlags::MAP_ANONYMOUS) {
+        if pgoffset != 0 {
+            return Err(EINVAL);
+        }
+
+        if !is_shared {
+            Mapping::Anonymous
+        } else {
+            // The mode is unimportant here, since we are checking prot in mm_area.
+            let shared_area = Task::block_on(SHM_MANAGER.lock()).create_shared_area(len, 0x777);
+            Mapping::File(FileMapping::new(shared_area.area.clone(), 0, len))
+        }
+    } else {
+        let file = thread
+            .files
+            .get(fd)
+            .ok_or(EBADF)?
+            .get_inode()?
+            .ok_or(EBADF)?;
+
+        Mapping::File(FileMapping::new(file, pgoffset, len))
+    };
+
+    let permission = Permission {
+        read: prot.contains(UserMmapProtocol::PROT_READ),
+        write: prot.contains(UserMmapProtocol::PROT_WRITE),
+        execute: prot.contains(UserMmapProtocol::PROT_EXEC),
+    };
 
     // TODO!!!: If we are doing mmap's in 32-bit mode, we should check whether
     //          `addr` is above user reachable memory.
     let addr = if flags.contains(UserMmapFlags::MAP_FIXED) {
         if prot.is_empty() {
-            Task::block_on(mm_list.protect(
-                addr,
-                len,
-                Permission {
-                    read: prot.contains(UserMmapProtocol::PROT_READ),
-                    write: prot.contains(UserMmapProtocol::PROT_WRITE),
-                    execute: prot.contains(UserMmapProtocol::PROT_EXEC),
-                },
-            ))
-            .map(|_| addr)
+            Task::block_on(mm_list.protect(addr, len, permission)).map(|_| addr)
         } else {
-            mm_list.mmap_fixed(
-                addr,
-                len,
-                Mapping::Anonymous,
-                Permission {
-                    read: prot.contains(UserMmapProtocol::PROT_READ),
-                    write: prot.contains(UserMmapProtocol::PROT_WRITE),
-                    execute: prot.contains(UserMmapProtocol::PROT_EXEC),
-                },
-            )
+            mm_list.mmap_fixed(addr, len, mapping, permission, is_shared)
         }
     } else {
-        mm_list.mmap_hint(
-            addr,
-            len,
-            Mapping::Anonymous,
-            Permission {
-                read: prot.contains(UserMmapProtocol::PROT_READ),
-                write: prot.contains(UserMmapProtocol::PROT_WRITE),
-                execute: prot.contains(UserMmapProtocol::PROT_EXEC),
-            },
-        )
+        mm_list.mmap_hint(addr, len, mapping, permission, is_shared)
     };
 
     addr.map(|addr| addr.addr())
@@ -108,7 +115,7 @@ fn mmap(
     len: usize,
     prot: UserMmapProtocol,
     flags: UserMmapFlags,
-    fd: u32,
+    fd: FD,
     offset: usize,
 ) -> KResult<usize> {
     do_mmap2(thread, addr, len, prot, flags, fd, offset / PAGE_SIZE)
@@ -121,7 +128,7 @@ fn mmap2(
     len: usize,
     prot: UserMmapProtocol,
     flags: UserMmapFlags,
-    fd: u32,
+    fd: FD,
     pgoffset: usize,
 ) -> KResult<usize> {
     do_mmap2(thread, addr, len, prot, flags, fd, pgoffset)
@@ -169,6 +176,85 @@ fn mprotect(addr: usize, len: usize, prot: UserMmapProtocol) -> KResult<()> {
     ))
 }
 
+#[eonix_macros::define_syscall(SYS_SHMGET)]
+fn shmget(key: usize, size: usize, shmflg: ShmFlags) -> KResult<u32> {
+    let size = size.align_up(PAGE_SIZE);
+
+    let mut shm_manager = Task::block_on(SHM_MANAGER.lock());
+    let shmid = gen_shm_id(key)?;
+
+    if key == IPC_PRIVATE {
+        let new_shm = shm_manager.create_shared_area(size, shmflg.bits() & 0x777);
+        shm_manager.insert(shmid, new_shm);
+        return Ok(shmid);
+    }
+
+    if let Some(_) = shm_manager.get(shmid) {
+        if shmflg.contains(ShmFlags::IPC_CREAT | ShmFlags::IPC_EXCL) {
+            return Err(EEXIST);
+        }
+
+        return Ok(shmid);
+    }
+
+    if shmflg.contains(ShmFlags::IPC_CREAT) {
+        let new_shm = shm_manager.create_shared_area(size, shmflg.bits() & 0x777);
+        shm_manager.insert(shmid, new_shm);
+        return Ok(shmid);
+    }
+
+    return Err(ENOENT);
+}
+
+#[eonix_macros::define_syscall(SYS_SHMAT)]
+fn shmat(shmid: u32, addr: usize, shmflg: ShmFlags) -> KResult<usize> {
+    let mm_list = &thread.process.mm_list;
+    let shm_manager = Task::block_on(SHM_MANAGER.lock());
+    let shm_area = shm_manager.get(shmid).ok_or(EINVAL)?;
+
+    let mut permission = Permission {
+        read: true,
+        write: true,
+        execute: false,
+    };
+
+    if shmflg.contains(ShmFlags::SHM_EXEC) {
+        permission.execute = true;
+    }
+    if shmflg.contains(ShmFlags::SHM_RDONLY) {
+        permission.write = false;
+    }
+
+    let mapping = Mapping::File(FileMapping {
+        file: shm_area.area.clone(),
+        offset: 0,
+        length: shm_area.size,
+    });
+
+    let addr = if addr != 0 {
+        if addr % PAGE_SIZE != 0 && !shmflg.contains(ShmFlags::SHM_RND) {
+            return Err(EINVAL);
+        }
+        let addr = VAddr::from(addr.align_down(PAGE_SIZE));
+        mm_list.mmap_fixed(addr, shm_area.size, mapping, permission, true)
+    } else {
+        mm_list.mmap_hint(VAddr::NULL, shm_area.size, mapping, permission, true)
+    }?;
+
+    thread.process.shm_areas.lock().insert(addr, shm_area.size);
+
+    Ok(addr.addr())
+}
+
+#[eonix_macros::define_syscall(SYS_SHMDT)]
+fn shmdt(addr: usize) -> KResult<usize> {
+    let addr = VAddr::from(addr);
+    let mut shm_areas = thread.process.shm_areas.lock();
+    let size = *shm_areas.get(&addr).ok_or(EINVAL)?;
+    shm_areas.remove(&addr);
+    return Task::block_on(thread.process.mm_list.unmap(addr, size)).map(|_| 0);
+}
+
 #[eonix_macros::define_syscall(SYS_MEMBARRIER)]
 fn membarrier(_cmd: usize, _flags: usize) -> KResult<()> {
     Ok(())

+ 4 - 1
src/kernel/task/loader/elf.rs

@@ -274,6 +274,7 @@ impl<E: ElfArch> Elf<E> {
                 write: true,
                 execute: false,
             },
+            false,
         )?;
 
         StackInitializer::new(&mm_list, E::STACK_BASE_ADDR, args, envs, aux_vec).init()
@@ -356,11 +357,12 @@ impl<E: ElfArch> Elf<E> {
                 vmap_start,
                 file_len,
                 Mapping::File(FileMapping::new(
-                    self.file.clone(),
+                    self.file.get_inode()?,
                     file_offset,
                     real_file_length,
                 )),
                 permission,
+                false,
             )?;
         }
 
@@ -370,6 +372,7 @@ impl<E: ElfArch> Elf<E> {
                 vmem_len - file_len,
                 Mapping::Anonymous,
                 permission,
+                false,
             )?;
         }
 

+ 4 - 0
src/kernel/task/process.rs

@@ -15,6 +15,7 @@ use alloc::{
     sync::{Arc, Weak},
 };
 use core::sync::atomic::{AtomicU32, Ordering};
+use eonix_mm::address::VAddr;
 use eonix_runtime::task::Task;
 use eonix_sync::{
     AsProof as _, AsProofMut as _, Locked, Proof, ProofMut, RwLockReadGuard, SpinGuard,
@@ -47,6 +48,8 @@ pub struct Process {
 
     pub exit_signal: Option<Signal>,
 
+    pub shm_areas: Spin<BTreeMap<VAddr, usize>>,
+
     /// Parent process
     ///
     /// `parent` must be valid during the whole life of the process.
@@ -221,6 +224,7 @@ impl ProcessBuilder {
             pid: self.pid.expect("should set pid before building"),
             wait_list: WaitList::new(),
             mm_list,
+            shm_areas: Spin::new(BTreeMap::new()),
             exit_signal: self.exit_signal,
             parent: RCUPointer::empty(),
             pgroup: RCUPointer::empty(),

+ 10 - 0
src/kernel/vfs/file.rs

@@ -11,6 +11,7 @@ use crate::{
         task::Thread,
         terminal::{Terminal, TerminalIORequest},
         user::{UserPointer, UserPointerMut},
+        vfs::inode::Inode,
         CharDevice,
     },
     prelude::*,
@@ -86,6 +87,15 @@ pub struct File {
     file_type: FileType,
 }
 
+impl File {
+    pub fn get_inode(&self) -> KResult<Option<Arc<dyn Inode>>> {
+        match &self.file_type {
+            FileType::Inode(inode_file) => Ok(Some(inode_file.dentry.get_inode()?)),
+            _ => Ok(None),
+        }
+    }
+}
+
 pub enum SeekOption {
     Set(usize),
     Current(isize),