implement epoll
This commit is contained in:
parent
7bd2ce50f2
commit
f9121a025e
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user