diff --git a/src/libos/src/fs/io_multiplexing.rs b/src/libos/src/fs/io_multiplexing.rs index 2316bff9..456ae88d 100644 --- a/src/libos/src/fs/io_multiplexing.rs +++ b/src/libos/src/fs/io_multiplexing.rs @@ -1,6 +1,9 @@ use super::*; use crate::syscall::AsSocket; use std::vec::Vec; +use std::collections::btree_map::BTreeMap; +use std::fmt; +use std::any::Any; /// Forward to host `poll` /// (sgx_libc doesn't have `select`) @@ -100,6 +103,56 @@ pub fn do_poll( } } +pub fn do_epoll_create1(flags: c_int) -> Result { + info!("epoll_create1: flags: {}", flags); + + let epoll = EpollFile::new()?; + let file_ref: Arc> = Arc::new(Box::new(epoll)); + let current_ref = process::get_current(); + let mut proc = current_ref.lock().unwrap(); + let fd = { + let close_on_spawn = flags & libc::EPOLL_CLOEXEC != 0; + proc.get_files().lock().unwrap().put(file_ref, close_on_spawn) + }; + Ok(fd) +} + +pub fn do_epoll_ctl( + epfd: FileDesc, + op: EpollOp, + fd: FileDesc, +) -> Result<(), Error> { + info!("epoll_ctl: epfd: {}, op: {:?}, fd: {}", epfd, op, fd); + + let current_ref = process::get_current(); + let mut proc = current_ref.lock().unwrap(); + let mut file_ref = proc.get_files().lock().unwrap().get(epfd)?; + let mut epoll = file_ref.as_epoll()?.inner.lock().unwrap(); + + match op { + EpollOp::Add(event) => epoll.add(fd, event)?, + EpollOp::Modify(event) => epoll.modify(fd, event)?, + EpollOp::Delete => epoll.remove(fd)?, + } + Ok(()) +} + +pub fn do_epoll_wait( + epfd: FileDesc, + events: &mut [libc::epoll_event], + timeout: c_int, +) -> Result { + info!("epoll_wait: epfd: {}, len: {:?}, timeout: {}", epfd, events.len(), timeout); + + let current_ref = process::get_current(); + let mut proc = current_ref.lock().unwrap(); + let mut file_ref = proc.get_files().lock().unwrap().get(epfd)?; + let mut epoll = file_ref.as_epoll()?.inner.lock().unwrap(); + + let count = epoll.wait(events, timeout)?; + Ok(count) +} + /// Safe methods for `libc::fd_set` trait FdSetExt { fn set(&mut self, fd: usize); @@ -126,3 +179,234 @@ impl FdSetExt for libc::fd_set { unsafe { libc::FD_ISSET(fd as c_int, self) } } } + +pub enum EpollOp { + Add(libc::epoll_event), + Modify(libc::epoll_event), + Delete +} + +impl Debug for EpollOp { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + EpollOp::Add(_) => "Add", + EpollOp::Modify(_) => "Modify", + EpollOp::Delete => "Delete", + }; + write!(f, "{}", s) + } +} + +pub struct EpollFile { + inner: SgxMutex, +} + +impl EpollFile { + pub fn new() -> Result { + Ok(Self { + inner: SgxMutex::new(EpollFileInner::new()?) + }) + } +} + +struct EpollFileInner { + epoll_fd: c_int, + fd_to_host: BTreeMap, + fd_to_libos: BTreeMap, +} + +// FIXME: What if a Linux fd is closed but still in an epoll? +impl EpollFileInner { + /// Create a new Linux epoll file descriptor + pub fn new() -> Result { + let ret = unsafe { libc::ocall::epoll_create1(0) }; + if ret < 0 { + return Err(Error::new(Errno::from_retval(ret as i32), "")); + } + Ok(EpollFileInner { + epoll_fd: ret, + fd_to_host: BTreeMap::new(), + fd_to_libos: BTreeMap::new(), + }) + } + + /// Add `fd` to the interest list and associate the settings + /// specified in `event` with the internal file linked to `fd`. + pub fn add(&mut self, fd: FileDesc, mut event: libc::epoll_event) -> Result<(), Error> { + if self.fd_to_host.contains_key(&fd) { + return Err(Error::new(EEXIST, "fd is exist in epoll")); + } + // query host fd + let current_ref = process::get_current(); + let mut proc = current_ref.lock().unwrap(); + let file_ref = proc.get_files().lock().unwrap().get(fd)?; + let host_fd = file_ref.as_socket()?.fd() as FileDesc; + + let ret = unsafe { + libc::ocall::epoll_ctl( + self.epoll_fd, + libc::EPOLL_CTL_ADD, + host_fd as c_int, + &mut event, + ) + }; + if ret < 0 { + return Err(Error::new(Errno::from_retval(ret as i32), "")); + } + self.fd_to_host.insert(fd, host_fd); + self.fd_to_libos.insert(host_fd, fd); + Ok(()) + } + + /// Change the settings associated with `fd` in the interest list to + /// the new settings specified in `event`. + pub fn modify(&mut self, fd: FileDesc, mut event: libc::epoll_event) -> Result<(), Error> { + let host_fd = *self.fd_to_host.get(&fd) + .ok_or(Error::new(EINVAL, "fd is not exist in epoll"))?; + let ret = unsafe { + libc::ocall::epoll_ctl( + self.epoll_fd, + libc::EPOLL_CTL_MOD, + host_fd as c_int, + &mut event, + ) + }; + if ret < 0 { + return Err(Error::new(Errno::from_retval(ret as i32), "")); + } + Ok(()) + } + + /// Remove the target file descriptor `fd` from the interest list. + pub fn remove(&mut self, fd: FileDesc) -> Result<(), Error> { + let host_fd = *self.fd_to_host.get(&fd) + .ok_or(Error::new(EINVAL, "fd is not exist in epoll"))?; + let ret = unsafe { + libc::ocall::epoll_ctl( + self.epoll_fd, + libc::EPOLL_CTL_DEL, + host_fd as c_int, + core::ptr::null_mut(), + ) + }; + if ret < 0 { + return Err(Error::new(Errno::from_retval(ret as i32), "")); + } + self.fd_to_host.remove(&fd); + self.fd_to_libos.remove(&host_fd); + Ok(()) + } + + /// Wait for an I/O event on the epoll. + /// Returns the number of file descriptors ready for the requested I/O. + pub fn wait( + &mut self, + events: &mut [libc::epoll_event], + timeout: c_int, + ) -> Result { + let ret = unsafe { + libc::ocall::epoll_wait( + self.epoll_fd, + events.as_mut_ptr(), + events.len() as c_int, + timeout, + ) + }; + if ret < 0 { + return Err(Error::new(Errno::from_retval(ret as i32), "")); + } + // convert host fd to libos + let count = ret as usize; + for event in events[0..count].iter_mut() { + let host_fd = event.u64 as FileDesc; + let fd = self.fd_to_libos[&host_fd]; + event.u64 = fd as u64; + } + Ok(count) + } +} + +impl Drop for EpollFileInner { + fn drop(&mut self) { + unsafe { + libc::ocall::close(self.epoll_fd); + } + } +} + +impl File for EpollFile { + fn read(&self, buf: &mut [u8]) -> Result { + unimplemented!() + } + + fn write(&self, buf: &[u8]) -> Result { + unimplemented!() + } + + fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result { + unimplemented!() + } + + fn write_at(&self, offset: usize, buf: &[u8]) -> Result { + unimplemented!() + } + + fn readv(&self, bufs: &mut [&mut [u8]]) -> Result { + unimplemented!() + } + + fn writev(&self, bufs: &[&[u8]]) -> Result { + unimplemented!() + } + + fn seek(&self, pos: SeekFrom) -> Result { + Err(Error::new(Errno::ESPIPE, "Epoll does not support seek")) + } + + fn metadata(&self) -> Result { + unimplemented!() + } + + fn set_len(&self, len: u64) -> Result<(), Error> { + unimplemented!() + } + + fn sync_all(&self) -> Result<(), Error> { + unimplemented!() + } + + fn sync_data(&self) -> Result<(), Error> { + unimplemented!() + } + + fn read_entry(&self) -> Result { + unimplemented!() + } + + fn as_any(&self) -> &Any { + self + } +} + +impl Debug for EpollFile { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let inner = self.inner.lock().unwrap(); + f.debug_struct("EpollFile") + .field("epoll_fd", &inner.epoll_fd) + .field("fds", &inner.fd_to_host.keys()) + .field("host_fds", &inner.fd_to_host.values()) + .finish() + } +} + +pub trait AsEpoll { + fn as_epoll(&self) -> Result<&EpollFile, Error>; +} + +impl AsEpoll for FileRef { + fn as_epoll(&self) -> Result<&EpollFile, Error> { + self.as_any() + .downcast_ref::() + .ok_or(Error::new(Errno::EBADF, "not a epoll")) + } +} diff --git a/src/libos/src/syscall/mod.rs b/src/libos/src/syscall/mod.rs index 2d75663f..6b0c793c 100644 --- a/src/libos/src/syscall/mod.rs +++ b/src/libos/src/syscall/mod.rs @@ -7,7 +7,7 @@ //! 3. Dispatch the syscall to `do_*` (at this file) //! 4. Do some memory checks then call `mod::do_*` (at each module) -use fs::{File, SocketFile, FileDesc, FileRef}; +use fs::{File, SocketFile, FileDesc, FileRef, EpollOp}; use prelude::*; use process::{ChildProcessFilter, FileAction, pid_t, CloneFlags, FutexFlags, FutexOp}; use std::ffi::{CStr, CString}; @@ -94,7 +94,8 @@ pub extern "C" fn dispatch_syscall( arg1 as libc::nfds_t, arg2 as c_int, ), - SYS_EPOLL_CREATE => do_epoll_create1(arg0 as c_int), + SYS_EPOLL_CREATE => do_epoll_create(arg0 as c_int), + SYS_EPOLL_CREATE1 => do_epoll_create1(arg0 as c_int), SYS_EPOLL_CTL => do_epoll_ctl( arg0 as c_int, arg1 as c_int, @@ -1021,8 +1022,16 @@ fn do_poll( Ok(n as isize) } +fn do_epoll_create(size: c_int) -> Result { + if size <= 0 { + return Err(Error::new(EINVAL, "size is not positive")); + } + do_epoll_create1(0) +} + fn do_epoll_create1(flags: c_int) -> Result { - unimplemented!() + let fd = fs::do_epoll_create1(flags)?; + Ok(fd as isize) } fn do_epoll_ctl( @@ -1031,7 +1040,20 @@ fn do_epoll_ctl( fd: c_int, event: *const libc::epoll_event, ) -> Result { - unimplemented!() + let event = if event.is_null() { + None + } else { + check_ptr(event)?; + Some(unsafe { event.read() }) + }; + let op = match (op, event) { + (libc::EPOLL_CTL_ADD, Some(event)) => EpollOp::Add(event), + (libc::EPOLL_CTL_MOD, Some(event)) => EpollOp::Modify(event), + (libc::EPOLL_CTL_DEL, _) => EpollOp::Delete, + _ => return Err(Error::new(EINVAL, "invalid epoll op or event ptr")), + }; + fs::do_epoll_ctl(epfd as FileDesc, op, fd as FileDesc)?; + Ok(0) } fn do_epoll_wait( @@ -1040,7 +1062,18 @@ fn do_epoll_wait( maxevents: c_int, timeout: c_int, ) -> Result { - unimplemented!() + let maxevents = { + if maxevents <= 0 { + return Err(Error::new(EINVAL, "maxevents <= 0")); + } + maxevents as usize + }; + let events = { + check_mut_array(events, maxevents)?; + unsafe { std::slice::from_raw_parts_mut(events, maxevents) } + }; + let count = fs::do_epoll_wait(epfd as FileDesc, events, timeout)?; + Ok(count as isize) } pub trait AsSocket {