implement epoll

This commit is contained in:
WangRunji 2019-04-09 01:40:30 +08:00
parent 7bd2ce50f2
commit f9121a025e
2 changed files with 322 additions and 5 deletions

@ -1,6 +1,9 @@
use super::*; use super::*;
use crate::syscall::AsSocket; use crate::syscall::AsSocket;
use std::vec::Vec; use std::vec::Vec;
use std::collections::btree_map::BTreeMap;
use std::fmt;
use std::any::Any;
/// Forward to host `poll` /// Forward to host `poll`
/// (sgx_libc doesn't have `select`) /// (sgx_libc doesn't have `select`)
@ -100,6 +103,56 @@ pub fn do_poll(
} }
} }
pub fn do_epoll_create1(flags: c_int) -> Result<FileDesc, Error> {
info!("epoll_create1: flags: {}", flags);
let epoll = EpollFile::new()?;
let file_ref: Arc<Box<File>> = 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<usize, Error> {
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` /// Safe methods for `libc::fd_set`
trait FdSetExt { trait FdSetExt {
fn set(&mut self, fd: usize); 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) } 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<EpollFileInner>,
}
impl EpollFile {
pub fn new() -> Result<Self, Error> {
Ok(Self {
inner: SgxMutex::new(EpollFileInner::new()?)
})
}
}
struct EpollFileInner {
epoll_fd: c_int,
fd_to_host: BTreeMap<FileDesc, FileDesc>,
fd_to_libos: BTreeMap<FileDesc, FileDesc>,
}
// 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<Self, Error> {
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<usize, Error> {
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<usize, Error> {
unimplemented!()
}
fn write(&self, buf: &[u8]) -> Result<usize, Error> {
unimplemented!()
}
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize, Error> {
unimplemented!()
}
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize, Error> {
unimplemented!()
}
fn readv(&self, bufs: &mut [&mut [u8]]) -> Result<usize, Error> {
unimplemented!()
}
fn writev(&self, bufs: &[&[u8]]) -> Result<usize, Error> {
unimplemented!()
}
fn seek(&self, pos: SeekFrom) -> Result<off_t, Error> {
Err(Error::new(Errno::ESPIPE, "Epoll does not support seek"))
}
fn metadata(&self) -> Result<Metadata, Error> {
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<String, Error> {
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::<EpollFile>()
.ok_or(Error::new(Errno::EBADF, "not a epoll"))
}
}

@ -7,7 +7,7 @@
//! 3. Dispatch the syscall to `do_*` (at this file) //! 3. Dispatch the syscall to `do_*` (at this file)
//! 4. Do some memory checks then call `mod::do_*` (at each module) //! 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 prelude::*;
use process::{ChildProcessFilter, FileAction, pid_t, CloneFlags, FutexFlags, FutexOp}; use process::{ChildProcessFilter, FileAction, pid_t, CloneFlags, FutexFlags, FutexOp};
use std::ffi::{CStr, CString}; use std::ffi::{CStr, CString};
@ -94,7 +94,8 @@ pub extern "C" fn dispatch_syscall(
arg1 as libc::nfds_t, arg1 as libc::nfds_t,
arg2 as c_int, 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( SYS_EPOLL_CTL => do_epoll_ctl(
arg0 as c_int, arg0 as c_int,
arg1 as c_int, arg1 as c_int,
@ -1021,8 +1022,16 @@ fn do_poll(
Ok(n as isize) Ok(n as isize)
} }
fn do_epoll_create(size: c_int) -> Result<isize, Error> {
if size <= 0 {
return Err(Error::new(EINVAL, "size is not positive"));
}
do_epoll_create1(0)
}
fn do_epoll_create1(flags: c_int) -> Result<isize, Error> { fn do_epoll_create1(flags: c_int) -> Result<isize, Error> {
unimplemented!() let fd = fs::do_epoll_create1(flags)?;
Ok(fd as isize)
} }
fn do_epoll_ctl( fn do_epoll_ctl(
@ -1031,7 +1040,20 @@ fn do_epoll_ctl(
fd: c_int, fd: c_int,
event: *const libc::epoll_event, event: *const libc::epoll_event,
) -> Result<isize, Error> { ) -> Result<isize, Error> {
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( fn do_epoll_wait(
@ -1040,7 +1062,18 @@ fn do_epoll_wait(
maxevents: c_int, maxevents: c_int,
timeout: c_int, timeout: c_int,
) -> Result<isize, Error> { ) -> Result<isize, Error> {
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 { pub trait AsSocket {