Skip to content

Commit

Permalink
Merge pull request #164 from yunwei37/master
Browse files Browse the repository at this point in the history
Add async select syscall
  • Loading branch information
wangrunji0408 authored Aug 28, 2020
2 parents e5d31cb + 8335a7e commit f4406b3
Show file tree
Hide file tree
Showing 8 changed files with 372 additions and 6 deletions.
16 changes: 16 additions & 0 deletions linux-loader/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ mod tests {
assert_eq!(test("/bin/busybox").await, 0);
}

#[should_panic]
#[async_std::test]
async fn test_entry_wrong() {
assert_eq!(test("/bin/busybos").await, 0);
}

#[async_std::test]
async fn test_uname() {
assert_eq!(test("/bin/busybox uname -a").await, 0);
Expand Down Expand Up @@ -200,4 +206,14 @@ mod tests {
async fn test_shm() {
assert_eq!(test("/bin/testshm1").await, 0);
}

#[async_std::test]
async fn test_select() {
assert_eq!(test("/bin/testselect").await, 0);
}

#[async_std::test]
async fn test_poll() {
assert_eq!(test("/bin/testpoll").await, 0);
}
}
6 changes: 6 additions & 0 deletions linux-object/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,12 @@ impl LinuxProcess {
inner.files.get(&fd).cloned().ok_or(LxError::EBADF)
}

/// get all files
pub fn get_files(&self) -> LxResult<HashMap<FileDesc, Arc<dyn FileLike>>> {
let inner = self.inner.lock();
Ok(inner.files.clone())
}

/// Close file descriptor `fd`.
pub fn close_file(&self, fd: FileDesc) -> LxResult {
let mut inner = self.inner.lock();
Expand Down
9 changes: 9 additions & 0 deletions linux-object/src/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ impl TimeVal {
pub fn now() -> TimeVal {
TimeSpec::now().into()
}
/// to msec
pub fn to_msec(&self) -> usize {
self.sec * 1_000 + self.usec / 1_000
}
}

impl TimeSpec {
Expand All @@ -54,6 +58,11 @@ impl TimeSpec {
inode.set_metadata(&metadata).ok();
}
}

/// to msec
pub fn to_msec(&self) -> usize {
self.sec * 1_000 + self.nsec / 1_000_000
}
}

impl Into<Timespec> for TimeSpec {
Expand Down
1 change: 1 addition & 0 deletions linux-syscall/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ linux-object = { path = "../linux-object" }
kernel-hal = { path = "../kernel-hal" }
rcore-fs = { git = "https://github.com/rcore-os/rcore-fs", rev = "517af47" }
lazy_static = { version = "1.4", features = ["spin_no_std"] }
bitvec = { version = "0.17", default-features = false, features = ["alloc"] }
225 changes: 222 additions & 3 deletions linux-syscall/src/file/poll.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
//! IO Multiplex operations
//!
//! - select4
//! - select, pselect
//! - poll, ppoll
//! - epoll: create, ctl, wait
use super::*;
use alloc::boxed::Box;
use alloc::vec::Vec;
use bitvec::prelude::{BitVec, Lsb0};
use core::future::Future;
use core::mem::size_of;
use core::pin::Pin;
use core::task::{Context, Poll};
use core::time::Duration;
use kernel_hal::timer_set;
use linux_object::fs::FileDesc;
use linux_object::time::*;

Expand All @@ -29,9 +32,13 @@ impl Syscall<'_> {
#[must_use = "future does nothing unless polled/`await`-ed"]
struct PollFuture<'a> {
polls: &'a mut Vec<PollFd>,
timeout_msecs: usize,
begin_time_ms: usize,
syscall: &'a Syscall<'a>,
}

let begin_time_ms = TimeVal::now().to_msec();

impl<'a> Future for PollFuture<'a> {
type Output = SysResult;

Expand Down Expand Up @@ -71,11 +78,33 @@ impl Syscall<'_> {
if events > 0 {
return Poll::Ready(Ok(events));
}

if self.timeout_msecs == 0 {
// no timeout, return now;
return Poll::Ready(Ok(0));
} else {
let waker = cx.waker().clone();
timer_set(
Duration::from_millis(self.timeout_msecs as u64),
Box::new(move |_| waker.wake()),
);
}

let current_time_ms = TimeVal::now().to_msec();
// infinity check
if self.timeout_msecs < (1 << 31)
&& current_time_ms - self.begin_time_ms >= self.timeout_msecs as usize
{
return Poll::Ready(Ok(0));
}

Poll::Pending
}
}
let future = PollFuture {
polls: &mut polls,
timeout_msecs,
begin_time_ms,
syscall: self,
};
let result = future.await;
Expand All @@ -96,11 +125,138 @@ impl Syscall<'_> {
1 << 31 // infinity
} else {
let timeout = timeout.read().unwrap();
timeout.sec * 1_000 + timeout.nsec / 1_000_000
timeout.to_msec()
};

self.sys_poll(ufds, nfds, timeout_msecs as usize).await
}

/// similar to select, but have sigmask argument
pub async fn sys_pselect6(
&mut self,
nfds: usize,
read: UserInOutPtr<u32>,
write: UserInOutPtr<u32>,
err: UserInOutPtr<u32>,
timeout: UserInPtr<TimeVal>,
_sigset: usize,
) -> SysResult {
self.sys_select(nfds, read, write, err, timeout).await
}

/// allow a program to monitor multiple file descriptors,
/// waiting until one or more of the file descriptors become "ready" for some class of I/O operation.
///
/// A file descriptor is considered ready if it is possible to perform the corresponding I/O operation (e.g., read) without blocking.
pub async fn sys_select(
&mut self,
nfds: usize,
read: UserInOutPtr<u32>,
write: UserInOutPtr<u32>,
err: UserInOutPtr<u32>,
timeout: UserInPtr<TimeVal>,
) -> SysResult {
info!(
"select: nfds: {}, read: {:?}, write: {:?}, err: {:?}, timeout: {:?}",
nfds, read, write, err, timeout
);
if nfds as u64 == 0 {
return Ok(0);
}
let mut read_fds = FdSet::new(read, nfds)?;
let mut write_fds = FdSet::new(write, nfds)?;
let mut err_fds = FdSet::new(err, nfds)?;

let timeout_msecs = if !timeout.is_null() {
let timeout = timeout.read()?;
timeout.to_msec()
} else {
// infinity
1 << 31
};
let begin_time_ms = TimeVal::now().to_msec();

#[must_use = "future does nothing unless polled/`await`-ed"]
struct SelectFuture<'a> {
read_fds: &'a mut FdSet,
write_fds: &'a mut FdSet,
err_fds: &'a mut FdSet,
timeout_msecs: usize,
begin_time_ms: usize,
syscall: &'a Syscall<'a>,
}

impl<'a> Future for SelectFuture<'a> {
type Output = SysResult;

fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let files = self.syscall.linux_process().get_files()?;

let mut events = 0;
for (&fd, file_like) in files.iter() {
if !self.err_fds.contains(fd)
&& !self.read_fds.contains(fd)
&& !self.write_fds.contains(fd)
{
continue;
}
let mut fut = Box::pin(file_like.async_poll());
let status = match fut.as_mut().poll(cx) {
Poll::Ready(Ok(ret)) => ret,
Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
Poll::Pending => continue,
};
if status.error && self.err_fds.contains(fd) {
self.err_fds.set(fd);
events += 1;
}
if status.read && self.read_fds.contains(fd) {
self.read_fds.set(fd);
events += 1;
}
if status.write && self.write_fds.contains(fd) {
self.write_fds.set(fd);
events += 1;
}
}

// some event happens, so evoke the process
if events > 0 {
return Poll::Ready(Ok(events));
}

if self.timeout_msecs == 0 {
// no timeout, return now;
return Poll::Ready(Ok(0));
} else {
let waker = cx.waker().clone();
timer_set(
Duration::from_millis(self.timeout_msecs as u64),
Box::new(move |_| waker.wake()),
);
}

let current_time_ms = TimeVal::now().to_msec();
// infinity check
if self.timeout_msecs < (1 << 31)
&& current_time_ms - self.begin_time_ms >= self.timeout_msecs as usize
{
return Poll::Ready(Ok(0));
}

Poll::Pending
}
}
let future = SelectFuture {
read_fds: &mut read_fds,
write_fds: &mut write_fds,
err_fds: &mut err_fds,
timeout_msecs,
begin_time_ms,
syscall: self,
};
future.await
}
}

#[repr(C)]
Expand All @@ -125,3 +281,66 @@ bitflags! {
const INVAL = 0x0020;
}
}

/// fd size per item
const FD_PER_ITEM: usize = 8 * size_of::<u32>();
/// max Fdset size
const MAX_FDSET_SIZE: usize = 1024 / FD_PER_ITEM;

/// FdSet data struct for select
struct FdSet {
/// input addr, for update Fdset use
addr: UserInOutPtr<u32>,
/// FdSet bit buffer
origin: BitVec<Lsb0, u32>,
}

impl FdSet {
/// Initialize a `FdSet` from pointer and number of fds
/// Check if the array is large enough
fn new(mut addr: UserInOutPtr<u32>, nfds: usize) -> Result<FdSet, LxError> {
if addr.is_null() {
Ok(FdSet {
addr,
origin: BitVec::new(),
})
} else {
let len = (nfds + FD_PER_ITEM - 1) / FD_PER_ITEM;
if len > MAX_FDSET_SIZE {
return Err(LxError::EINVAL);
}
let slice = addr.read_array(len)?;

// save the fdset, and clear it
let origin = BitVec::from_vec(slice);
let mut vec0 = Vec::<u32>::new();
vec0.resize(len, 0);
addr.write_array(&vec0)?;
Ok(FdSet { addr, origin })
}
}

/// Try to set fd in `FdSet`
/// Return true when `FdSet` is valid, and false when `FdSet` is bad (i.e. null pointer)
/// Fd should be less than nfds
fn set(&mut self, fd: FileDesc) -> bool {
let fd: usize = fd.into();
if self.origin.is_empty() {
return false;
}
self.origin.set(fd, true);
let vec: Vec<u32> = self.origin.clone().into();
self.addr.write_array(&vec).is_ok()
}

/// Check to see whether `fd` is in original `FdSet`
/// Fd should be less than nfds
fn contains(&self, fd: FileDesc) -> bool {
let fd: usize = fd.into();
if fd < self.origin.len() {
self.origin[fd]
} else {
false
}
}
}
12 changes: 9 additions & 3 deletions linux-syscall/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,10 @@ impl Syscall<'_> {
}

// io multiplexing
// Sys::PSELECT6 => self.sys_pselect6(a0, a1.into(), a2.into(), a3.into(), a4.into(), a5.into()),
Sys::PSELECT6 => {
self.sys_pselect6(a0, a1.into(), a2.into(), a3.into(), a4.into(), a5)
.await
}
Sys::PPOLL => self.sys_ppoll(a0.into(), a1, a2.into()).await, // ignore sigmask
// Sys::EPOLL_CREATE1 => self.sys_epoll_create1(a0),
// Sys::EPOLL_CTL => self.sys_epoll_ctl(a0, a1, a2, a3.into()),
Expand Down Expand Up @@ -244,15 +247,18 @@ impl Syscall<'_> {
#[cfg(target_arch = "x86_64")]
/// syscall specified for x86_64
async fn x86_64_syscall(&mut self, sys_type: Sys, args: [usize; 6]) -> SysResult {
let [a0, a1, a2, _a3, _a4, _a5] = args;
let [a0, a1, a2, a3, a4, _a5] = args;
match sys_type {
Sys::OPEN => self.sys_open(a0.into(), a1, a2),
Sys::STAT => self.sys_stat(a0.into(), a1.into()),
Sys::LSTAT => self.sys_lstat(a0.into(), a1.into()),
Sys::POLL => self.sys_poll(a0.into(), a1, a2).await,
Sys::ACCESS => self.sys_access(a0.into(), a1),
Sys::PIPE => self.sys_pipe(a0.into()),
// Sys::SELECT => self.sys_select(a0, a1.into(), a2.into(), a3.into(), a4.into()),
Sys::SELECT => {
self.sys_select(a0, a1.into(), a2.into(), a3.into(), a4.into())
.await
}
Sys::DUP2 => self.sys_dup2(a0.into(), a1.into()),
// Sys::ALARM => self.unimplemented("alarm", Ok(0)),
Sys::FORK => self.sys_fork(),
Expand Down
Loading

0 comments on commit f4406b3

Please sign in to comment.