[libos] Add support for SHM
This commit is contained in:
		
							parent
							
								
									c84c3b7b88
								
							
						
					
					
						commit
						ed96ce55dd
					
				
							
								
								
									
										7
									
								
								src/libos/src/ipc/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										7
									
								
								src/libos/src/ipc/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | use super::*; | ||||||
|  | 
 | ||||||
|  | mod shm; | ||||||
|  | mod syscalls; | ||||||
|  | 
 | ||||||
|  | pub use self::shm::{key_t, shmids_t, SHM_MANAGER}; | ||||||
|  | pub use self::syscalls::{do_shmat, do_shmctl, do_shmdt, do_shmget}; | ||||||
							
								
								
									
										509
									
								
								src/libos/src/ipc/shm.rs
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										509
									
								
								src/libos/src/ipc/shm.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,509 @@ | |||||||
|  | use super::*; | ||||||
|  | 
 | ||||||
|  | use crate::fs::FileMode; | ||||||
|  | use crate::process::{do_getegid, do_geteuid, gid_t, uid_t, ThreadRef}; | ||||||
|  | use crate::time::{do_gettimeofday, time_t}; | ||||||
|  | use crate::vm::{ | ||||||
|  |     ChunkRef, VMInitializer, VMMapOptionsBuilder, VMPerms, VMRange, USER_SPACE_VM_MANAGER, | ||||||
|  | }; | ||||||
|  | use std::collections::{HashMap, HashSet}; | ||||||
|  | 
 | ||||||
|  | #[allow(non_camel_case_types)] | ||||||
|  | pub type key_t = u32; | ||||||
|  | pub type ShmId = u32; | ||||||
|  | pub type CmdId = u32; | ||||||
|  | 
 | ||||||
|  | // min shared seg size (bytes)
 | ||||||
|  | const SHMMIN: usize = 1; | ||||||
|  | // max shared seg size (bytes)
 | ||||||
|  | const SHMMAX: usize = (usize::MAX - (1_usize << 24)); | ||||||
|  | // max num of segs system wide,
 | ||||||
|  | // also indicates the max shmid - 1 in Occlum
 | ||||||
|  | const SHMMNI: ShmId = 4096; | ||||||
|  | 
 | ||||||
|  | const IPC_PRIVATE: key_t = 0; | ||||||
|  | 
 | ||||||
|  | // For cmd in shmctl()
 | ||||||
|  | const IPC_RMID: CmdId = 0; | ||||||
|  | const IPC_SET: CmdId = 1; | ||||||
|  | const IPC_STAT: CmdId = 2; | ||||||
|  | const IPC_INFO: CmdId = 3; | ||||||
|  | const SHM_LOCK: CmdId = 11; | ||||||
|  | const SHM_UNLOCK: CmdId = 12; | ||||||
|  | const SHM_STAT: CmdId = 13; | ||||||
|  | const SHM_INFO: CmdId = 14; | ||||||
|  | const SHM_STAT_ANY: CmdId = 15; | ||||||
|  | 
 | ||||||
|  | #[allow(non_camel_case_types)] | ||||||
|  | #[derive(Debug)] | ||||||
|  | #[repr(C)] | ||||||
|  | struct ipc_perm_t { | ||||||
|  |     key: key_t, | ||||||
|  |     uid: uid_t, | ||||||
|  |     gid: gid_t, | ||||||
|  |     cuid: uid_t, | ||||||
|  |     cgid: gid_t, | ||||||
|  |     mode: u16, | ||||||
|  |     pad1: u16, | ||||||
|  |     seq: u16, | ||||||
|  |     pad2: u16, | ||||||
|  |     unused1: u64, | ||||||
|  |     unused2: u64, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[allow(non_camel_case_types)] | ||||||
|  | #[derive(Debug)] | ||||||
|  | #[repr(C)] | ||||||
|  | pub struct shmids_t { | ||||||
|  |     shm_perm: ipc_perm_t, | ||||||
|  |     shm_segsz: size_t, | ||||||
|  |     shm_atime: time_t, | ||||||
|  |     shm_dtime: time_t, | ||||||
|  |     shm_ctime: time_t, | ||||||
|  |     shm_cpid: pid_t, | ||||||
|  |     shm_lpid: pid_t, | ||||||
|  |     shm_nattach: u64, | ||||||
|  |     unused1: u64, | ||||||
|  |     unused2: u64, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // shared memory segment status
 | ||||||
|  | bitflags! { | ||||||
|  |     struct ShmStatus : u16 { | ||||||
|  |         // segment will be destroyed on last detach
 | ||||||
|  |         const SHM_DEST = 0o1000; | ||||||
|  |         // segment will not be swapped, unused now
 | ||||||
|  |         const SHM_LOCKED = 0o2000; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bitflags! { | ||||||
|  |     pub struct ShmFlags: u32 { | ||||||
|  |         const IPC_CREAT = 0o1000; | ||||||
|  |         const IPC_EXCL = 0o2000; | ||||||
|  | 
 | ||||||
|  |         /// read by owner
 | ||||||
|  |         const S_IRUSR = FileMode::S_IRUSR.bits() as u32; | ||||||
|  |         /// write by owner
 | ||||||
|  |         const S_IWUSR = FileMode::S_IWUSR.bits() as u32; | ||||||
|  |         /// execute/search by owner
 | ||||||
|  |         const S_IXUSR = FileMode::S_IXUSR.bits() as u32; | ||||||
|  |         /// read by group
 | ||||||
|  |         const S_IRGRP = FileMode::S_IRGRP.bits() as u32; | ||||||
|  |         /// write by group
 | ||||||
|  |         const S_IWGRP = FileMode::S_IWGRP.bits() as u32; | ||||||
|  |         /// execute/search by group
 | ||||||
|  |         const S_IXGRP = FileMode::S_IXGRP.bits() as u32; | ||||||
|  |         /// read by others
 | ||||||
|  |         const S_IROTH = FileMode::S_IROTH.bits() as u32; | ||||||
|  |         /// write by others
 | ||||||
|  |         const S_IWOTH = FileMode::S_IWOTH.bits() as u32; | ||||||
|  |         /// execute/search by others
 | ||||||
|  |         const S_IXOTH = FileMode::S_IXOTH.bits() as u32; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl ShmFlags { | ||||||
|  |     fn to_file_mode(&self) -> FileMode { | ||||||
|  |         let mut shmflgs = *self; | ||||||
|  |         shmflgs.remove(ShmFlags::IPC_CREAT); | ||||||
|  |         shmflgs.remove(ShmFlags::IPC_EXCL); | ||||||
|  |         let file_mode = FileMode::from_bits(shmflgs.bits as u16 & FileMode::all().bits()).unwrap(); | ||||||
|  |         file_mode | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | struct ShmSegment { | ||||||
|  |     shmid: ShmId, | ||||||
|  |     key: key_t, | ||||||
|  | 
 | ||||||
|  |     uid: uid_t, | ||||||
|  |     gid: gid_t, | ||||||
|  |     cuid: uid_t, | ||||||
|  |     cgid: gid_t, | ||||||
|  |     mode: FileMode, | ||||||
|  |     status: ShmStatus, | ||||||
|  | 
 | ||||||
|  |     shm_atime: time_t, | ||||||
|  |     shm_dtime: time_t, | ||||||
|  |     shm_ctime: time_t, | ||||||
|  | 
 | ||||||
|  |     shm_cpid: pid_t, | ||||||
|  |     shm_lpid: pid_t, | ||||||
|  | 
 | ||||||
|  |     shm_nattach: u64, | ||||||
|  |     chunk: ChunkRef, | ||||||
|  |     process_set: HashSet<pid_t>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl ShmSegment { | ||||||
|  |     fn new(shmid: ShmId, key: key_t, size: size_t, mode: FileMode) -> Result<Self> { | ||||||
|  |         let vm_option = VMMapOptionsBuilder::default() | ||||||
|  |             .size(size) | ||||||
|  |             // Currently, Occlum only support shared memory segment with rw permission
 | ||||||
|  |             .perms(VMPerms::READ | VMPerms::WRITE) | ||||||
|  |             .initializer(VMInitializer::FillZeros()) | ||||||
|  |             .build()?; | ||||||
|  |         let chunk = USER_SPACE_VM_MANAGER.internal().mmap_chunk(&vm_option)?; | ||||||
|  | 
 | ||||||
|  |         Ok(ShmSegment { | ||||||
|  |             shmid: shmid, | ||||||
|  |             key: key, | ||||||
|  |             uid: do_geteuid().unwrap() as u32, | ||||||
|  |             cuid: do_geteuid().unwrap() as u32, | ||||||
|  |             gid: do_getegid().unwrap() as u32, | ||||||
|  |             cgid: do_getegid().unwrap() as u32, | ||||||
|  |             mode: mode, | ||||||
|  |             status: ShmStatus::empty(), | ||||||
|  |             shm_atime: 0, | ||||||
|  |             shm_dtime: 0, | ||||||
|  |             shm_ctime: ShmManager::current_time(), | ||||||
|  |             shm_cpid: current!().process().pid(), | ||||||
|  |             shm_lpid: 0, | ||||||
|  |             shm_nattach: 0, | ||||||
|  |             chunk: chunk, | ||||||
|  |             process_set: HashSet::new(), | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn check_perm(&self) -> Result<()> { | ||||||
|  |         // TODO: Add permission control
 | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn set_destruction(&mut self) { | ||||||
|  |         self.status.insert(ShmStatus::SHM_DEST) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn is_destruction(&self) -> bool { | ||||||
|  |         self.status.contains(ShmStatus::SHM_DEST) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn shm_start(&self) -> usize { | ||||||
|  |         self.chunk.range().start() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn shm_size(&self) -> usize { | ||||||
|  |         self.chunk.range().size() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn shm_add_pid(&mut self, pid: &pid_t) -> Result<()> { | ||||||
|  |         if self.process_set.contains(pid) { | ||||||
|  |             return_errno!(EINVAL, "this pid has been attached to the shm"); | ||||||
|  |         } | ||||||
|  |         self.process_set.insert(*pid); | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn shm_remove_pid(&mut self, pid: &pid_t) -> Result<()> { | ||||||
|  |         if !self.process_set.contains(pid) { | ||||||
|  |             return_errno!(EINVAL, " this pid has not been attached to the shm"); | ||||||
|  |         } | ||||||
|  |         self.process_set.remove(&pid); | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Drop for ShmSegment { | ||||||
|  |     fn drop(&mut self) { | ||||||
|  |         debug!("drop shm: {:?}", self); | ||||||
|  |         assert!(self.shm_nattach == 0); | ||||||
|  |         assert!(self.process_set.len() == 0); | ||||||
|  |         USER_SPACE_VM_MANAGER | ||||||
|  |             .internal() | ||||||
|  |             .munmap_chunk(&self.chunk, None); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | struct ShmIdManager { | ||||||
|  |     used_id: HashSet<ShmId>, | ||||||
|  |     free_num: u32, | ||||||
|  |     last_alloc_id: ShmId, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl ShmIdManager { | ||||||
|  |     fn new() -> Self { | ||||||
|  |         let used_id = HashSet::new(); | ||||||
|  |         let free_num = SHMMNI as u32; | ||||||
|  |         let last_alloc_id = SHMMNI - 1; | ||||||
|  |         ShmIdManager { | ||||||
|  |             used_id, | ||||||
|  |             free_num, | ||||||
|  |             last_alloc_id, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Always return next free id for shmid
 | ||||||
|  |     fn get_new_shmid(&mut self) -> Result<ShmId> { | ||||||
|  |         if self.free_num == 0 { | ||||||
|  |             return_errno!(ENOSPC, "all possible shared memory IDs have been taken"); | ||||||
|  |         } else { | ||||||
|  |             self.free_num -= 1; | ||||||
|  |         } | ||||||
|  |         let mut id = self.last_alloc_id + 1; | ||||||
|  |         loop { | ||||||
|  |             if id == SHMMNI { | ||||||
|  |                 id = 0; | ||||||
|  |             } | ||||||
|  |             if !self.used_id.contains(&id) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             id += 1; | ||||||
|  |         } | ||||||
|  |         self.last_alloc_id = id; | ||||||
|  |         Ok(id) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn free_shmid(&mut self, shmid: &ShmId) -> Result<()> { | ||||||
|  |         self.free_num += 1; | ||||||
|  |         self.used_id.remove(shmid); | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | lazy_static! { | ||||||
|  |     pub static ref SHM_MANAGER: ShmManager = ShmManager::new(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub struct ShmManager { | ||||||
|  |     shm_segments: RwLock<HashMap<ShmId, ShmSegment>>, | ||||||
|  |     shmid_manager: RwLock<ShmIdManager>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl ShmManager { | ||||||
|  |     fn new() -> Self { | ||||||
|  |         ShmManager { | ||||||
|  |             shm_segments: RwLock::new(HashMap::new()), | ||||||
|  |             shmid_manager: RwLock::new(ShmIdManager::new()), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn current_time() -> time_t { | ||||||
|  |         do_gettimeofday().sec() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn get_new_shmid(&self) -> Result<ShmId> { | ||||||
|  |         let mut shmid_manager = self.shmid_manager.write().unwrap(); | ||||||
|  |         shmid_manager.get_new_shmid() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn free_shmid(&self, shmid: &ShmId) -> Result<()> { | ||||||
|  |         let mut shmid_manager = self.shmid_manager.write().unwrap(); | ||||||
|  |         shmid_manager.free_shmid(&shmid) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn shmctl_rmshm(&self, shmid: ShmId) -> Result<()> { | ||||||
|  |         let mut shm_segments = self.shm_segments.write().unwrap(); | ||||||
|  |         let shm = shm_segments.get_mut(&shmid); | ||||||
|  | 
 | ||||||
|  |         if let Some(shm) = shm { | ||||||
|  |             shm.shm_ctime = ShmManager::current_time(); | ||||||
|  |             shm.set_destruction(); | ||||||
|  |             if shm.shm_nattach == 0 { | ||||||
|  |                 let shmid = shm.shmid; | ||||||
|  |                 self.free_shmid(&shmid)?; | ||||||
|  |                 shm_segments.remove(&shmid); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             return_errno!(EINVAL, "cannot find shm by shmid"); | ||||||
|  |         } | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn shmctl_ipcstat(&self, shmid: ShmId, buf: Option<&mut shmids_t>) -> Result<()> { | ||||||
|  |         let shm_segments = self.shm_segments.read().unwrap(); | ||||||
|  |         let shm = shm_segments.get(&shmid); | ||||||
|  |         if let Some(shm) = shm { | ||||||
|  |             shm.check_perm()?; | ||||||
|  |             let mut buf = match buf { | ||||||
|  |                 Some(buf) => buf, | ||||||
|  |                 None => return_errno!(EFAULT, "buf is empty"), | ||||||
|  |             }; | ||||||
|  |             let shm_perm = ipc_perm_t { | ||||||
|  |                 key: shm.key, | ||||||
|  |                 uid: shm.uid, | ||||||
|  |                 gid: shm.uid, | ||||||
|  |                 cuid: shm.cuid, | ||||||
|  |                 cgid: shm.cgid, | ||||||
|  |                 mode: shm.mode.bits() | shm.status.bits(), | ||||||
|  |                 pad1: 0, | ||||||
|  |                 seq: 0, | ||||||
|  |                 pad2: 0, | ||||||
|  |                 unused1: 0, | ||||||
|  |                 unused2: 0, | ||||||
|  |             }; | ||||||
|  |             let shmids = shmids_t { | ||||||
|  |                 shm_perm: shm_perm, | ||||||
|  |                 shm_segsz: shm.shm_size(), | ||||||
|  |                 shm_atime: shm.shm_atime, | ||||||
|  |                 shm_dtime: shm.shm_dtime, | ||||||
|  |                 shm_ctime: shm.shm_ctime, | ||||||
|  |                 shm_cpid: shm.shm_cpid, | ||||||
|  |                 shm_lpid: shm.shm_lpid, | ||||||
|  |                 shm_nattach: shm.shm_nattach, | ||||||
|  |                 unused1: 0, | ||||||
|  |                 unused2: 0, | ||||||
|  |             }; | ||||||
|  |             *buf = shmids; | ||||||
|  |         } else { | ||||||
|  |             return_errno!(EINVAL, "cannot find shm by shmid"); | ||||||
|  |         } | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn do_shmget(&self, key: key_t, size: usize, shmflg: ShmFlags) -> Result<ShmId> { | ||||||
|  |         debug!( | ||||||
|  |             "do_shmget: key: {:?}, size: {:?}, shmflg: {:?}", | ||||||
|  |             key, size, shmflg | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         // Check the size from user
 | ||||||
|  |         if size < SHMMIN || size > SHMMAX { | ||||||
|  |             return_errno!(EINVAL, "invalid size"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let mut mode = shmflg.to_file_mode(); | ||||||
|  |         // The creator and user must have the read and write permission to the segment
 | ||||||
|  |         let read_per = mode.contains(FileMode::S_IRUSR) | ||||||
|  |             || mode.contains(FileMode::S_IRGRP) | ||||||
|  |             || mode.contains(FileMode::S_IROTH); | ||||||
|  |         let write_per = mode.contains(FileMode::S_IWUSR) | ||||||
|  |             || mode.contains(FileMode::S_IWGRP) | ||||||
|  |             || mode.contains(FileMode::S_IWOTH); | ||||||
|  |         if !(read_per && write_per) { | ||||||
|  |             return_errno!( | ||||||
|  |                 EINVAL, | ||||||
|  |                 "shared memory segement in occlum should have rw permission now" | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let mut shm_segments = self.shm_segments.write().unwrap(); | ||||||
|  |         let shmid = if key == IPC_PRIVATE { | ||||||
|  |             let shmid = self.get_new_shmid()?; | ||||||
|  |             let shm = ShmSegment::new(shmid, key, size, mode)?; | ||||||
|  |             shm_segments.insert(shm.shmid, shm); | ||||||
|  |             shmid | ||||||
|  |         } else { | ||||||
|  |             // Get the shm from key if the segment is not marked to be destroyed
 | ||||||
|  |             let shm = shm_segments | ||||||
|  |                 .values() | ||||||
|  |                 .find(|&shm| !shm.is_destruction() && shm.key == key); | ||||||
|  |             let shmid = if let Some(shm) = shm { | ||||||
|  |                 if shmflg.contains(ShmFlags::IPC_CREAT) && shmflg.contains(ShmFlags::IPC_EXCL) { | ||||||
|  |                     return_errno!( | ||||||
|  |                         EEXIST, | ||||||
|  |                         "the shared memory segment already exists for given key" | ||||||
|  |                     ); | ||||||
|  |                 } | ||||||
|  |                 // The size from user must be less than the actual size
 | ||||||
|  |                 if size > shm.shm_size() { | ||||||
|  |                     return_errno!(EINVAL, "the size from user is too large"); | ||||||
|  |                 } | ||||||
|  |                 // Check the permission
 | ||||||
|  |                 shm.check_perm()?; | ||||||
|  |                 shm.shmid | ||||||
|  |             } else { | ||||||
|  |                 if !shmflg.contains(ShmFlags::IPC_CREAT) { | ||||||
|  |                     return_errno!(ENOENT, "no segment exists for given key"); | ||||||
|  |                 } | ||||||
|  |                 let shmid = self.get_new_shmid()?; | ||||||
|  |                 let shm = ShmSegment::new(shmid, key, size, mode)?; | ||||||
|  |                 shm_segments.insert(shm.shmid, shm); | ||||||
|  |                 shmid | ||||||
|  |             }; | ||||||
|  |             shmid | ||||||
|  |         }; | ||||||
|  |         Ok(shmid) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn do_shmat(&self, shmid: ShmId, addr: usize, shmflg: ShmFlags) -> Result<usize> { | ||||||
|  |         debug!( | ||||||
|  |             "do_shmat: shmid: {:?}, addr: {:?}, shmflg: {:?}", | ||||||
|  |             shmid, addr, shmflg | ||||||
|  |         ); | ||||||
|  |         let pid = current!().process().pid(); | ||||||
|  |         let mut shm_segments = self.shm_segments.write().unwrap(); | ||||||
|  |         let shm = shm_segments.get_mut(&shmid); | ||||||
|  |         let addr = if let Some(shm) = shm { | ||||||
|  |             if addr != 0 && addr != shm.shm_start() { | ||||||
|  |                 return_errno!(EINVAL, "invalid addr"); | ||||||
|  |             } | ||||||
|  |             // Check the permission
 | ||||||
|  |             shm.check_perm()?; | ||||||
|  | 
 | ||||||
|  |             shm.shm_nattach += 1; | ||||||
|  |             shm.shm_atime = ShmManager::current_time(); | ||||||
|  |             shm.shm_add_pid(&pid)?; | ||||||
|  |             shm.shm_lpid = pid; | ||||||
|  |             shm.shm_start() | ||||||
|  |         } else { | ||||||
|  |             return_errno!(EINVAL, "cannot find shm by shmid"); | ||||||
|  |         }; | ||||||
|  |         Ok(addr) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn do_shmdt(&self, addr: usize) -> Result<()> { | ||||||
|  |         debug!("do_shmdt: addr: {:?}", addr); | ||||||
|  |         let pid = current!().process().pid(); | ||||||
|  |         let mut shm_segments = self.shm_segments.write().unwrap(); | ||||||
|  |         let shm = shm_segments | ||||||
|  |             .values_mut() | ||||||
|  |             .find(|shm| shm.shm_start() == addr); | ||||||
|  | 
 | ||||||
|  |         if let Some(shm) = shm { | ||||||
|  |             shm.shm_dtime = ShmManager::current_time(); | ||||||
|  |             shm.shm_lpid = pid; | ||||||
|  |             shm.shm_remove_pid(&pid)?; | ||||||
|  |             shm.shm_nattach -= 1; | ||||||
|  |             if shm.is_destruction() && shm.shm_nattach == 0 { | ||||||
|  |                 let shmid = shm.shmid; | ||||||
|  |                 self.free_shmid(&shmid); | ||||||
|  |                 shm_segments.remove(&shmid); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             return_errno!(EINVAL, "cannot find shm by addr"); | ||||||
|  |         } | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn do_shmctl(&self, shmid: ShmId, cmd: CmdId, buf: Option<&mut shmids_t>) -> Result<()> { | ||||||
|  |         debug!( | ||||||
|  |             "do_shmctl: shmid: {:?}, cmd: {:?}, buf: {:?}", | ||||||
|  |             shmid, cmd, buf | ||||||
|  |         ); | ||||||
|  |         match cmd { | ||||||
|  |             IPC_RMID => self.shmctl_rmshm(shmid), | ||||||
|  |             IPC_STAT => self.shmctl_ipcstat(shmid, buf), | ||||||
|  |             _ => return_errno!(EINVAL, "unimplemented cmd"), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn detach_shm_when_process_exit(&self, thread: &ThreadRef) { | ||||||
|  |         let pid = &thread.process().pid(); | ||||||
|  |         let mut shm_segments = self.shm_segments.write().unwrap(); | ||||||
|  |         shm_segments.drain_filter(|shmid, shm| match shm.shm_remove_pid(&pid) { | ||||||
|  |             // There exists a shm that has been attached to the current process
 | ||||||
|  |             Ok(_) => { | ||||||
|  |                 shm.shm_nattach -= 1; | ||||||
|  |                 if shm.is_destruction() && shm.shm_nattach == 0 { | ||||||
|  |                     self.free_shmid(&shmid); | ||||||
|  |                     true | ||||||
|  |                 } else { | ||||||
|  |                     false | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             Err(_) => false, | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn clean_when_libos_exit(&self) { | ||||||
|  |         let mut shm_segments = self.shm_segments.write().unwrap(); | ||||||
|  |         for (_, shm) in shm_segments.drain() { | ||||||
|  |             debug!("clean shm: {:?}", shm); | ||||||
|  |             self.free_shmid(&shm.shmid); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										36
									
								
								src/libos/src/ipc/syscalls.rs
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										36
									
								
								src/libos/src/ipc/syscalls.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | |||||||
|  | use super::*; | ||||||
|  | 
 | ||||||
|  | use util::mem_util::from_user; | ||||||
|  | 
 | ||||||
|  | use super::shm::{shmids_t, CmdId, ShmFlags, ShmId, SHM_MANAGER}; | ||||||
|  | 
 | ||||||
|  | pub fn do_shmget(key: key_t, size: size_t, shmflg: i32) -> Result<isize> { | ||||||
|  |     let shmflg = | ||||||
|  |         ShmFlags::from_bits(shmflg as u32).ok_or_else(|| errno!(EINVAL, "invalid flags"))?; | ||||||
|  |     let shmid = SHM_MANAGER.do_shmget(key, size, shmflg)?; | ||||||
|  |     Ok(shmid as isize) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn do_shmat(shmid: i32, shmaddr: usize, shmflg: i32) -> Result<isize> { | ||||||
|  |     let shmflg = | ||||||
|  |         ShmFlags::from_bits(shmflg as u32).ok_or_else(|| errno!(EINVAL, "invalid flags"))?; | ||||||
|  |     let addr = SHM_MANAGER.do_shmat(shmid as ShmId, shmaddr, shmflg)?; | ||||||
|  |     Ok(addr as isize) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn do_shmdt(shmaddr: usize) -> Result<isize> { | ||||||
|  |     SHM_MANAGER.do_shmdt(shmaddr)?; | ||||||
|  |     Ok(0) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn do_shmctl(shmid: i32, cmd: i32, buf_u: *mut shmids_t) -> Result<isize> { | ||||||
|  |     let buf = if !buf_u.is_null() { | ||||||
|  |         from_user::check_mut_ptr(buf_u)?; | ||||||
|  |         let mut buf = unsafe { &mut *buf_u }; | ||||||
|  |         Some(buf) | ||||||
|  |     } else { | ||||||
|  |         None | ||||||
|  |     }; | ||||||
|  |     SHM_MANAGER.do_shmctl(shmid as ShmId, cmd as CmdId, buf)?; | ||||||
|  |     Ok(0) | ||||||
|  | } | ||||||
| @ -81,6 +81,7 @@ mod events; | |||||||
| mod exception; | mod exception; | ||||||
| mod fs; | mod fs; | ||||||
| mod interrupt; | mod interrupt; | ||||||
|  | mod ipc; | ||||||
| mod misc; | mod misc; | ||||||
| mod net; | mod net; | ||||||
| mod process; | mod process; | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ use super::do_vfork::{is_vforked_child_process, vfork_return_to_parent}; | |||||||
| use super::pgrp::clean_pgrp_when_exit; | use super::pgrp::clean_pgrp_when_exit; | ||||||
| use super::process::{Process, ProcessFilter}; | use super::process::{Process, ProcessFilter}; | ||||||
| use super::{table, ProcessRef, TermStatus, ThreadRef, ThreadStatus}; | use super::{table, ProcessRef, TermStatus, ThreadRef, ThreadStatus}; | ||||||
|  | use crate::ipc::SHM_MANAGER; | ||||||
| use crate::prelude::*; | use crate::prelude::*; | ||||||
| use crate::signal::{KernelSignal, SigNum}; | use crate::signal::{KernelSignal, SigNum}; | ||||||
| use crate::syscall::CpuContext; | use crate::syscall::CpuContext; | ||||||
| @ -107,6 +108,7 @@ fn exit_process(thread: &ThreadRef, term_status: TermStatus) { | |||||||
|     let mut process_inner = process.inner(); |     let mut process_inner = process.inner(); | ||||||
|     // Clean used VM
 |     // Clean used VM
 | ||||||
|     USER_SPACE_VM_MANAGER.free_chunks_when_exit(thread); |     USER_SPACE_VM_MANAGER.free_chunks_when_exit(thread); | ||||||
|  |     SHM_MANAGER.detach_shm_when_process_exit(thread); | ||||||
| 
 | 
 | ||||||
|     // The parent is the idle process
 |     // The parent is the idle process
 | ||||||
|     if parent_inner.is_none() { |     if parent_inner.is_none() { | ||||||
|  | |||||||
| @ -68,6 +68,8 @@ pub mod task; | |||||||
| pub type pid_t = u32; | pub type pid_t = u32; | ||||||
| #[allow(non_camel_case_types)] | #[allow(non_camel_case_types)] | ||||||
| pub type uid_t = u32; | pub type uid_t = u32; | ||||||
|  | #[allow(non_camel_case_types)] | ||||||
|  | pub type gid_t = u32; | ||||||
| 
 | 
 | ||||||
| pub type ProcessRef = Arc<Process>; | pub type ProcessRef = Arc<Process>; | ||||||
| pub type ThreadRef = Arc<Thread>; | pub type ThreadRef = Arc<Thread>; | ||||||
|  | |||||||
| @ -35,6 +35,7 @@ use crate::fs::{ | |||||||
|     FileRef, HostStdioFds, Stat, Statfs, |     FileRef, HostStdioFds, Stat, Statfs, | ||||||
| }; | }; | ||||||
| use crate::interrupt::{do_handle_interrupt, sgx_interrupt_info_t}; | use crate::interrupt::{do_handle_interrupt, sgx_interrupt_info_t}; | ||||||
|  | use crate::ipc::{do_shmat, do_shmctl, do_shmdt, do_shmget, key_t, shmids_t}; | ||||||
| use crate::misc::{resource_t, rlimit_t, sysinfo_t, utsname_t, RandFlags}; | use crate::misc::{resource_t, rlimit_t, sysinfo_t, utsname_t, RandFlags}; | ||||||
| use crate::net::{ | use crate::net::{ | ||||||
|     do_accept, do_accept4, do_bind, do_connect, do_epoll_create, do_epoll_create1, do_epoll_ctl, |     do_accept, do_accept4, do_bind, do_connect, do_epoll_create, do_epoll_create1, do_epoll_ctl, | ||||||
| @ -118,9 +119,9 @@ macro_rules! process_syscall_table_with_callback { | |||||||
|             (Msync = 26) => do_msync(addr: usize, size: usize, flags: u32), |             (Msync = 26) => do_msync(addr: usize, size: usize, flags: u32), | ||||||
|             (Mincore = 27) => handle_unsupported(), |             (Mincore = 27) => handle_unsupported(), | ||||||
|             (Madvise = 28) => handle_unsupported(), |             (Madvise = 28) => handle_unsupported(), | ||||||
|             (Shmget = 29) => handle_unsupported(), |             (Shmget = 29) => do_shmget(key: key_t, size: size_t, shmflg: i32), | ||||||
|             (Shmat = 30) => handle_unsupported(), |             (Shmat = 30) => do_shmat(shmid: i32, shmaddr: usize, shmflg: i32), | ||||||
|             (Shmctl = 31) => handle_unsupported(), |             (Shmctl = 31) => do_shmctl(shmid: i32, cmd: i32, buf: *mut shmids_t), | ||||||
|             (Dup = 32) => do_dup(old_fd: FileDesc), |             (Dup = 32) => do_dup(old_fd: FileDesc), | ||||||
|             (Dup2 = 33) => do_dup2(old_fd: FileDesc, new_fd: FileDesc), |             (Dup2 = 33) => do_dup2(old_fd: FileDesc, new_fd: FileDesc), | ||||||
|             (Pause = 34) => handle_unsupported(), |             (Pause = 34) => handle_unsupported(), | ||||||
| @ -156,7 +157,7 @@ macro_rules! process_syscall_table_with_callback { | |||||||
|             (Semget = 64) => handle_unsupported(), |             (Semget = 64) => handle_unsupported(), | ||||||
|             (Semop = 65) => handle_unsupported(), |             (Semop = 65) => handle_unsupported(), | ||||||
|             (Semctl = 66) => handle_unsupported(), |             (Semctl = 66) => handle_unsupported(), | ||||||
|             (Shmdt = 67) => handle_unsupported(), |             (Shmdt = 67) => do_shmdt(shmaddr: usize), | ||||||
|             (Msgget = 68) => handle_unsupported(), |             (Msgget = 68) => handle_unsupported(), | ||||||
|             (Msgsnd = 69) => handle_unsupported(), |             (Msgsnd = 69) => handle_unsupported(), | ||||||
|             (Msgrcv = 70) => handle_unsupported(), |             (Msgrcv = 70) => handle_unsupported(), | ||||||
|  | |||||||
| @ -75,10 +75,12 @@ mod vm_util; | |||||||
| 
 | 
 | ||||||
| use self::vm_layout::VMLayout; | use self::vm_layout::VMLayout; | ||||||
| 
 | 
 | ||||||
|  | pub use self::chunk::ChunkRef; | ||||||
| pub use self::process_vm::{MMapFlags, MRemapFlags, MSyncFlags, ProcessVM, ProcessVMBuilder}; | pub use self::process_vm::{MMapFlags, MRemapFlags, MSyncFlags, ProcessVM, ProcessVMBuilder}; | ||||||
| pub use self::user_space_vm::USER_SPACE_VM_MANAGER; | pub use self::user_space_vm::USER_SPACE_VM_MANAGER; | ||||||
| pub use self::vm_perms::VMPerms; | pub use self::vm_perms::VMPerms; | ||||||
| pub use self::vm_range::VMRange; | pub use self::vm_range::VMRange; | ||||||
|  | pub use self::vm_util::{VMInitializer, VMMapOptionsBuilder}; | ||||||
| 
 | 
 | ||||||
| pub fn do_mmap( | pub fn do_mmap( | ||||||
|     addr: usize, |     addr: usize, | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ use super::*; | |||||||
| 
 | 
 | ||||||
| use super::chunk::*; | use super::chunk::*; | ||||||
| use super::config; | use super::config; | ||||||
|  | use super::ipc::SHM_MANAGER; | ||||||
| use super::process::elf_file::{ElfFile, ProgramHeaderExt}; | use super::process::elf_file::{ElfFile, ProgramHeaderExt}; | ||||||
| use super::user_space_vm::USER_SPACE_VM_MANAGER; | use super::user_space_vm::USER_SPACE_VM_MANAGER; | ||||||
| use super::vm_area::VMArea; | use super::vm_area::VMArea; | ||||||
|  | |||||||
| @ -1,3 +1,4 @@ | |||||||
|  | use super::ipc::SHM_MANAGER; | ||||||
| use super::*; | use super::*; | ||||||
| use crate::ctor::dtor; | use crate::ctor::dtor; | ||||||
| use config::LIBOS_CONFIG; | use config::LIBOS_CONFIG; | ||||||
| @ -46,6 +47,7 @@ impl UserSpaceVMManager { | |||||||
| // be called after the main function. Static variables are still safe to visit at this time.
 | // be called after the main function. Static variables are still safe to visit at this time.
 | ||||||
| #[dtor] | #[dtor] | ||||||
| fn free_user_space() { | fn free_user_space() { | ||||||
|  |     SHM_MANAGER.clean_when_libos_exit(); | ||||||
|     let range = USER_SPACE_VM_MANAGER.range(); |     let range = USER_SPACE_VM_MANAGER.range(); | ||||||
|     assert!(USER_SPACE_VM_MANAGER.verified_clean_when_exit()); |     assert!(USER_SPACE_VM_MANAGER.verified_clean_when_exit()); | ||||||
|     let addr = range.start() as *const c_void; |     let addr = range.start() as *const c_void; | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user