diff --git a/src/libos/src/fs/io_multiplexing.rs b/src/libos/src/fs/io_multiplexing.rs new file mode 100644 index 00000000..2316bff9 --- /dev/null +++ b/src/libos/src/fs/io_multiplexing.rs @@ -0,0 +1,128 @@ +use super::*; +use crate::syscall::AsSocket; +use std::vec::Vec; + +/// Forward to host `poll` +/// (sgx_libc doesn't have `select`) +pub fn do_select( + nfds: usize, + readfds: &mut libc::fd_set, + writefds: &mut libc::fd_set, + exceptfds: &mut libc::fd_set, + timeout: Option, +) -> Result { + // convert libos fd to Linux fd + let mut host_to_libos_fd = [0; libc::FD_SETSIZE]; + let mut polls = Vec::::new(); + + let current_ref = process::get_current(); + let mut proc = current_ref.lock().unwrap(); + let file_table_ref = proc.get_files().lock().unwrap(); + + for fd in 0..nfds { + let (r, w, e) = (readfds.is_set(fd), writefds.is_set(fd), exceptfds.is_set(fd)); + if !(r || w || e) { + continue; + } + let host_fd = file_table_ref.get(fd as FileDesc)?.as_socket()?.fd(); + + host_to_libos_fd[host_fd as usize] = fd; + let mut events = 0; + if r { events |= libc::POLLIN; } + if w { events |= libc::POLLOUT; } + if e { events |= libc::POLLERR; } + + polls.push(libc::pollfd { + fd: host_fd as c_int, + events, + revents: 0, + }); + } + + let timeout = match timeout { + None => -1, + Some(tv) => (tv.tv_sec * 1000 + tv.tv_usec / 1000) as i32, + }; + + let ret = unsafe { + libc::ocall::poll(polls.as_mut_ptr(), polls.len() as u64, timeout) + }; + + if ret < 0 { + return Err(Error::new(Errno::from_retval(ret as i32), "")); + } + + // convert fd back and write fdset + readfds.clear(); + writefds.clear(); + exceptfds.clear(); + + for poll in polls.iter() { + let fd = host_to_libos_fd[poll.fd as usize]; + if poll.revents & libc::POLLIN != 0 { + readfds.set(fd); + } + if poll.revents & libc::POLLOUT != 0 { + writefds.set(fd); + } + if poll.revents & libc::POLLERR != 0 { + exceptfds.set(fd); + } + } + + Ok(ret as usize) +} + +pub fn do_poll( + polls: &mut [libc::pollfd], + timeout: c_int, +) -> Result { + info!("poll: [..], timeout: {}", timeout); + + let current_ref = process::get_current(); + let mut proc = current_ref.lock().unwrap(); + + // convert libos fd to Linux fd + for poll in polls.iter_mut() { + let file_ref = proc.get_files().lock().unwrap().get(poll.fd as FileDesc)?; + let socket = file_ref.as_socket()?; + poll.fd = socket.fd(); + } + let ret = unsafe { + libc::ocall::poll(polls.as_mut_ptr(), polls.len() as u64, timeout) + }; + // recover fd ? + + if ret < 0 { + Err(Error::new(Errno::from_retval(ret as i32), "")) + } else { + Ok(ret as usize) + } +} + +/// Safe methods for `libc::fd_set` +trait FdSetExt { + fn set(&mut self, fd: usize); + fn clear(&mut self); + fn is_set(&mut self, fd: usize) -> bool; +} + +impl FdSetExt for libc::fd_set { + fn set(&mut self, fd: usize) { + assert!(fd < libc::FD_SETSIZE); + unsafe { + libc::FD_SET(fd as c_int, self); + } + } + + fn clear(&mut self) { + unsafe { + libc::FD_ZERO(self); + } + } + + fn is_set(&mut self, fd: usize) -> bool { + assert!(fd < libc::FD_SETSIZE); + unsafe { libc::FD_ISSET(fd as c_int, self) } + } +} diff --git a/src/libos/src/fs/mod.rs b/src/libos/src/fs/mod.rs index 384a5f1a..e648e25a 100644 --- a/src/libos/src/fs/mod.rs +++ b/src/libos/src/fs/mod.rs @@ -12,6 +12,7 @@ pub use self::inode_file::{INodeExt, INodeFile, ROOT_INODE}; pub use self::socket_file::SocketFile; use self::inode_file::OpenOptions; pub use self::pipe::Pipe; +pub use self::io_multiplexing::*; mod file; mod file_table; @@ -19,6 +20,7 @@ mod inode_file; mod socket_file; mod pipe; mod sgx_impl; +mod io_multiplexing; pub fn do_open(path: &str, flags: u32, mode: u32) -> Result { diff --git a/src/libos/src/syscall/mod.rs b/src/libos/src/syscall/mod.rs index eb0fe921..2d75663f 100644 --- a/src/libos/src/syscall/mod.rs +++ b/src/libos/src/syscall/mod.rs @@ -81,6 +81,33 @@ pub extern "C" fn dispatch_syscall( SYS_LINK => do_link(arg0 as *const i8, arg1 as *const i8), SYS_UNLINK => do_unlink(arg0 as *const i8), + // IO multiplexing + SYS_SELECT => do_select( + arg0 as c_int, + arg1 as *mut libc::fd_set, + arg2 as *mut libc::fd_set, + arg3 as *mut libc::fd_set, + arg4 as *const libc::timeval, + ), + SYS_POLL => do_poll( + arg0 as *mut libc::pollfd, + arg1 as libc::nfds_t, + arg2 as c_int, + ), + SYS_EPOLL_CREATE => do_epoll_create1(arg0 as c_int), + SYS_EPOLL_CTL => do_epoll_ctl( + arg0 as c_int, + arg1 as c_int, + arg2 as c_int, + arg3 as *const libc::epoll_event, + ), + SYS_EPOLL_WAIT => do_epoll_wait( + arg0 as c_int, + arg1 as *mut libc::epoll_event, + arg2 as c_int, + arg3 as c_int, + ), + // process SYS_EXIT => do_exit(arg0 as i32), SYS_SPAWN => do_spawn( @@ -936,7 +963,87 @@ fn do_recvfrom( Ok(ret as isize) } -trait AsSocket { +fn do_select( + nfds: c_int, + readfds: *mut libc::fd_set, + writefds: *mut libc::fd_set, + exceptfds: *mut libc::fd_set, + timeout: *const libc::timeval +) -> Result { + // check arguments + if nfds < 0 || nfds >= libc::FD_SETSIZE as c_int { + return Err(Error::new(EINVAL, "nfds is negative or exceeds the resource limit")); + } + let nfds = nfds as usize; + + let mut zero_fds0: libc::fd_set = unsafe { core::mem::zeroed() }; + let mut zero_fds1: libc::fd_set = unsafe { core::mem::zeroed() }; + let mut zero_fds2: libc::fd_set = unsafe { core::mem::zeroed() }; + + let readfds = if !readfds.is_null() { + check_mut_ptr(readfds)?; + unsafe { &mut *readfds } + } else { + &mut zero_fds0 + }; + let writefds = if !writefds.is_null() { + check_mut_ptr(writefds)?; + unsafe { &mut *writefds } + } else { + &mut zero_fds1 + }; + let exceptfds = if !exceptfds.is_null() { + check_mut_ptr(exceptfds)?; + unsafe { &mut *exceptfds } + } else { + &mut zero_fds2 + }; + let timeout = if !timeout.is_null() { + check_ptr(timeout)?; + Some(unsafe { timeout.read() }) + } else { + None + }; + + let n = fs::do_select(nfds, readfds, writefds, exceptfds, timeout)?; + Ok(n as isize) +} + +fn do_poll( + fds: *mut libc::pollfd, + nfds: libc::nfds_t, + timeout: c_int, +) -> Result { + check_mut_array(fds, nfds as usize)?; + let polls = unsafe { std::slice::from_raw_parts_mut(fds, nfds as usize) }; + + let n = fs::do_poll(polls, timeout)?; + Ok(n as isize) +} + +fn do_epoll_create1(flags: c_int) -> Result { + unimplemented!() +} + +fn do_epoll_ctl( + epfd: c_int, + op: c_int, + fd: c_int, + event: *const libc::epoll_event, +) -> Result { + unimplemented!() +} + +fn do_epoll_wait( + epfd: c_int, + events: *mut libc::epoll_event, + maxevents: c_int, + timeout: c_int, +) -> Result { + unimplemented!() +} + +pub trait AsSocket { fn as_socket(&self) -> Result<&SocketFile, Error>; }