Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add async select syscall #164

Merged
merged 6 commits into from
Aug 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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