1
0
mirror of https://github.com/rcore-os/rCore.git synced 2024-11-22 08:06:17 +04:00

Merge pull request #26 from ssryps/master

implement epoll
This commit is contained in:
Runji Wang 2019-11-18 16:22:29 +08:00 committed by GitHub
commit 54fddfbe1d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 505 additions and 19 deletions

View File

@ -7,6 +7,8 @@ pub const MAX_PROCESS_NUM: usize = 128;
pub const USEC_PER_TICK: usize = 10000;
pub const INFORM_PER_MSEC: usize = 50;
lazy_static! {
pub static ref SMP_CORES: usize = {
if let Some(smp_str) = option_env!("SMP") {

128
kernel/src/fs/epoll.rs Normal file
View File

@ -0,0 +1,128 @@
use crate::fs::FileLike;
use crate::memory::MemorySet;
use crate::process::Process;
use crate::sync::{Condvar, SpinNoIrqLock};
use crate::syscall::{SysError, SysResult};
use alloc::{collections::BTreeMap, collections::BTreeSet};
use core::mem::size_of;
use core::slice;
pub struct EpollInstance {
pub events: BTreeMap<usize, EpollEvent>,
pub readyList: SpinNoIrqLock<BTreeSet<usize>>,
pub newCtlList: SpinNoIrqLock<BTreeSet<usize>>,
}
impl Clone for EpollInstance {
fn clone(&self) -> Self {
EpollInstance::new(0)
}
}
impl EpollInstance {
pub fn new(flags: usize) -> Self {
return EpollInstance {
events: BTreeMap::new(),
readyList: Default::default(),
newCtlList: Default::default(),
};
}
pub fn control(&mut self, op: usize, fd: usize, event: &EpollEvent) -> SysResult {
match (op as i32) {
EPollCtlOp::ADD => {
self.events.insert(fd, event.clone());
self.newCtlList.lock().insert(fd);
}
EPollCtlOp::MOD => {
if self.events.get(&fd).is_some() {
self.events.remove(&fd);
self.events.insert(fd, event.clone());
self.newCtlList.lock().insert(fd);
} else {
return Err(SysError::EPERM);
}
}
EPollCtlOp::DEL => {
if self.events.get(&fd).is_some() {
self.events.remove(&fd);
} else {
return Err(SysError::EPERM);
}
}
_ => {
return Err(SysError::EPERM);
}
}
Ok(0)
}
}
#[derive(Clone, Copy)]
pub struct EpollData {
ptr: u64,
}
#[repr(packed)]
#[derive(Clone)]
pub struct EpollEvent {
pub events: u32, /* Epoll events */
pub data: EpollData, /* User data variable */
}
impl EpollEvent {
pub const EPOLLIN: u32 = 0x001;
pub const EPOLLOUT: u32 = 0x004;
pub const EPOLLERR: u32 = 0x008;
pub const EPOLLHUP: u32 = 0x010;
pub const EPOLLPRI: u32 = 0x002;
pub const EPOLLRDNORM: u32 = 0x040;
pub const EPOLLRDBAND: u32 = 0x080;
pub const EPOLLWRNORM: u32 = 0x100;
pub const EPOLLWRBAND: u32 = 0x200;
pub const EPOLLMSG: u32 = 0x400;
pub const EPOLLRDHUP: u32 = 0x2000;
pub const EPOLLEXCLUSIVE: u32 = 1 << 28;
pub const EPOLLWAKEUP: u32 = 1 << 29;
pub const EPOLLONESHOT: u32 = 1 << 30;
pub const EPOLLET: u32 = 1 << 31;
pub fn contains(&self, events: u32) -> bool {
if (self.events & events) == 0 {
return false;
} else {
return true;
}
}
}
pub struct EPollCtlOp;
impl EPollCtlOp {
const ADD: i32 = 1; /* Add a file descriptor to the interface. */
const DEL: i32 = 2; /* Remove a file descriptor from the interface. */
const MOD: i32 = 3; /* Change file descriptor epoll_event structure. */
}
impl Process {
pub fn get_epoll_instance_mut(&mut self, fd: usize) -> Result<&mut EpollInstance, SysError> {
match self.get_file_like(fd)? {
FileLike::EpollInstance(instance) => Ok(instance),
_ => Err(SysError::EPERM),
}
}
pub fn get_epoll_instance(&self, fd: usize) -> Result<&EpollInstance, SysError> {
match self.files.get(&fd) {
Some(file_like) => match file_like {
FileLike::EpollInstance(instance) => Ok(&instance),
_ => Err(SysError::EPERM),
},
None => {
return Err(SysError::EPERM);
}
}
}
}

View File

@ -2,9 +2,12 @@ use core::fmt;
use super::ioctl::*;
use super::FileHandle;
use crate::fs::epoll::EpollInstance;
use crate::net::Socket;
use crate::sync::Condvar;
use crate::syscall::{SysError, SysResult};
use alloc::boxed::Box;
use alloc::vec::Vec;
use rcore_fs::vfs::PollStatus;
// TODO: merge FileLike to FileHandle ?
@ -13,6 +16,7 @@ use rcore_fs::vfs::PollStatus;
pub enum FileLike {
File(FileHandle),
Socket(Box<dyn Socket>),
EpollInstance(EpollInstance),
}
impl FileLike {
@ -20,6 +24,9 @@ impl FileLike {
let len = match self {
FileLike::File(file) => file.read(buf)?,
FileLike::Socket(socket) => socket.read(buf).0?,
FileLike::EpollInstance(instance) => {
return Err(SysError::ENOSYS);
}
};
Ok(len)
}
@ -27,6 +34,9 @@ impl FileLike {
let len = match self {
FileLike::File(file) => file.write(buf)?,
FileLike::Socket(socket) => socket.write(buf, None)?,
FileLike::EpollInstance(instance) => {
return Err(SysError::ENOSYS);
}
};
Ok(len)
}
@ -41,6 +51,9 @@ impl FileLike {
FileLike::Socket(socket) => {
socket.ioctl(request, arg1, arg2, arg3)?;
}
FileLike::EpollInstance(instance) => {
return Err(SysError::ENOSYS);
}
}
Ok(0)
}
@ -53,6 +66,9 @@ impl FileLike {
let (read, write, error) = socket.poll();
PollStatus { read, write, error }
}
FileLike::EpollInstance(instance) => {
return Err(SysError::ENOSYS);
}
};
Ok(status)
}
@ -63,6 +79,7 @@ impl FileLike {
FileLike::Socket(socket) => {
//TODO
}
FileLike::EpollInstance(instance) => {}
}
Ok(0)
}
@ -73,6 +90,7 @@ impl fmt::Debug for FileLike {
match self {
FileLike::File(file) => write!(f, "File({:?})", file),
FileLike::Socket(socket) => write!(f, "Socket({:?})", socket),
FileLike::EpollInstance(instance) => write!(f, "EpollInstance()"),
}
}
}

View File

@ -18,6 +18,7 @@ pub use self::stdio::{STDIN, STDOUT};
pub use self::vga::*;
mod device;
pub mod epoll;
mod file;
mod file_like;
mod ioctl;

View File

@ -6,6 +6,7 @@ use core::any::Any;
use rcore_fs::vfs::*;
use super::ioctl::*;
use crate::process::Process;
use crate::sync::Condvar;
use crate::sync::SpinNoIrqLock as Mutex;

View File

@ -175,7 +175,7 @@ impl Socket for TcpSocketState {
let mut sockets = SOCKETS.lock();
let mut socket = sockets.get::<TcpSocket>(self.handle.0);
if socket.is_open() {
if socket.may_recv() {
if let Ok(size) = socket.recv_slice(data) {
if size > 0 {
let endpoint = socket.remote_endpoint();
@ -428,7 +428,7 @@ impl Socket for UdpSocketState {
let mut sockets = SOCKETS.lock();
let mut socket = sockets.get::<UdpSocket>(self.handle.0);
if socket.is_open() {
if socket.can_recv() {
if let Ok((size, remote_endpoint)) = socket.recv_slice(data) {
let endpoint = remote_endpoint;
// avoid deadlock

View File

@ -1,13 +1,23 @@
use super::*;
use crate::process::processor;
use crate::process::Process;
use crate::process::{current_thread, processor};
use crate::thread;
use alloc::collections::VecDeque;
use alloc::sync::Arc;
use alloc::vec::Vec;
use rcore_thread::std_thread::Thread;
pub struct RegisteredProcess {
proc: Arc<SpinNoIrqLock<Process>>,
tid: usize,
epfd: usize,
fd: usize,
}
#[derive(Default)]
pub struct Condvar {
wait_queue: SpinNoIrqLock<VecDeque<Arc<thread::Thread>>>,
pub epoll_queue: SpinNoIrqLock<VecDeque<RegisteredProcess>>,
}
impl Condvar {
@ -91,12 +101,15 @@ impl Condvar {
pub fn notify_one(&self) {
if let Some(t) = self.wait_queue.lock().front() {
self.epoll_callback(t);
t.unpark();
}
}
pub fn notify_all(&self) {
let queue = self.wait_queue.lock();
for t in queue.iter() {
self.epoll_callback(t);
t.unpark();
}
}
@ -115,4 +128,51 @@ impl Condvar {
count
}
pub fn register_epoll_list(
&self,
proc: Arc<SpinNoIrqLock<Process>>,
tid: usize,
epfd: usize,
fd: usize,
) {
self.epoll_queue.lock().push_back(RegisteredProcess {
proc: proc,
tid: tid,
epfd: epfd,
fd: fd,
});
}
pub fn unregister_epoll_list(&self, tid: usize, epfd: usize, fd: usize) -> bool {
let mut epoll_list = self.epoll_queue.lock();
for idx in 0..epoll_list.len() {
if epoll_list[idx].tid == tid
&& epoll_list[idx].epfd == epfd
&& epoll_list[idx].fd == fd
{
epoll_list.remove(idx);
return true;
}
}
return false;
}
fn epoll_callback(&self, thread: &Arc<Thread>) {
let epoll_list = self.epoll_queue.lock();
for ist in epoll_list.iter() {
if thread.id() == ist.tid {
let mut proc = ist.proc.lock();
match proc.get_epoll_instance(ist.epfd) {
Ok(instacne) => {
let mut readylist = instacne.readyList.lock();
readylist.insert(ist.fd);
}
Err(r) => {
panic!("epoll instance not exist");
}
}
}
}
}
}

View File

@ -1,5 +1,4 @@
//! Syscalls for file system
use core::cell::UnsafeCell;
use core::cmp::min;
use core::mem::size_of;
@ -9,11 +8,19 @@ use rcore_fs::vfs::Timespec;
use crate::drivers::SOCKET_ACTIVITY;
use crate::fs::*;
use crate::memory::MemorySet;
use crate::sync::Condvar;
use crate::sync::{Condvar, SpinNoIrqLock};
use crate::trap::TICK_ACTIVITY;
use alloc::{collections::BTreeMap, collections::BTreeSet};
use bitvec::prelude::{BitSlice, BitVec, LittleEndian};
use super::*;
use crate::fs::epoll::EpollInstance;
use crate::net::server;
use crate::process::Process;
use alloc::collections::VecDeque;
use bitflags::_core::task::Poll;
use rcore_fs::vfs::PollStatus;
impl Syscall<'_> {
pub fn sys_read(&mut self, fd: usize, base: *mut u8, len: usize) -> SysResult {
@ -68,6 +75,8 @@ impl Syscall<'_> {
Ok(len)
}
/// sys_ppoll function is for handling the third argument of sys_poll.
pub fn sys_ppoll(
&mut self,
ufds: *mut PollFd,
@ -96,19 +105,20 @@ impl Syscall<'_> {
);
}
// check whether the fds is valid and is owned by this process
let mut condvars = alloc::vec![&(*TICK_ACTIVITY), &STDIN.pushed, &(*SOCKET_ACTIVITY)];
let polls = unsafe { self.vm().check_write_array(ufds, nfds)? };
for poll in polls.iter() {
if proc.files.get(&(poll.fd as usize)).is_none() {
return Err(SysError::EINVAL);
}
}
drop(proc);
let begin_time_ms = crate::trap::uptime_msec();
Condvar::wait_events(&[&STDIN.pushed, &(*SOCKET_ACTIVITY)], move || {
Condvar::wait_events(condvars.as_slice(), move || {
use PollEvents as PE;
let proc = self.process();
let mut events = 0;
// iterate each poll to check whether it is ready
for poll in polls.iter_mut() {
poll.revents = PE::empty();
if let Some(file_like) = proc.files.get(&(poll.fd as usize)) {
@ -135,11 +145,13 @@ impl Syscall<'_> {
}
drop(proc);
// some event happens, so evoke the process
if events > 0 {
return Some(Ok(events));
}
let current_time_ms = crate::trap::uptime_msec();
// time runs out, so the evoke the process
if timeout_msecs < (1 << 31) && current_time_ms - begin_time_ms > timeout_msecs {
return Some(Ok(0));
}
@ -186,6 +198,8 @@ impl Syscall<'_> {
1 << 31
};
let mut condvars = alloc::vec![&(*TICK_ACTIVITY), &STDIN.pushed, &(*SOCKET_ACTIVITY)];
// for debugging
if cfg!(debug_assertions) {
debug!("files before select {:#?}", proc.files);
@ -193,13 +207,13 @@ impl Syscall<'_> {
drop(proc);
let begin_time_ms = crate::trap::uptime_msec();
Condvar::wait_events(&[&STDIN.pushed, &(*SOCKET_ACTIVITY)], move || {
Condvar::wait_events(condvars.as_slice(), move || {
let proc = self.process();
let mut events = 0;
for (&fd, file_like) in proc.files.iter() {
if fd >= nfds {
continue;
}
// if fd >= nfds {
// continue;
// }
if !err_fds.contains(fd) && !read_fds.contains(fd) && !write_fds.contains(fd) {
continue;
}
@ -242,6 +256,238 @@ impl Syscall<'_> {
})
}
pub fn sys_epoll_create(&mut self, size: usize) -> SysResult {
info!("epoll_create: size: {:?}", size);
if (size as i32) < 0 {
return Err(SysError::EINVAL);
}
self.sys_epoll_create1(0)
}
pub fn sys_epoll_create1(&mut self, flags: usize) -> SysResult {
info!("epoll_create1: flags: {:?}", flags);
let mut proc = self.process();
let epollInstance = EpollInstance::new(flags);
let fd = proc.add_file(FileLike::EpollInstance(epollInstance));
Ok(fd)
}
pub fn sys_epoll_ctl(
&mut self,
epfd: usize,
op: usize,
fd: usize,
event: *mut EpollEvent,
) -> SysResult {
let mut proc = self.process();
if !proc.pid.is_init() {
// we trust pid 0 process
info!("sys_epoll_ctl: epfd: {}, op: {:?}, fd: {:#x}", epfd, op, fd);
}
let _event = unsafe { self.vm().check_read_ptr(event)? };
if proc.files.get(&fd).is_none() {
return Err(SysError::EPERM);
}
let epollInstance = match proc.get_epoll_instance_mut(epfd) {
Ok(ins) => ins,
Err(err) => {
return Err(err);
}
};
let ret = epollInstance.control(op, fd, &_event)?;
return Ok(ret);
}
pub fn sys_epoll_wait(
&mut self,
epfd: usize,
events: *mut EpollEvent,
maxevents: usize,
timeout: usize,
) -> SysResult {
self.sys_epoll_pwait(epfd, events, maxevents, timeout, 0)
}
pub fn sys_epoll_pwait(
&mut self,
epfd: usize,
events: *mut EpollEvent,
maxevents: usize,
timeout_msecs: usize,
sigset_t: usize,
) -> SysResult {
info!("epoll_pwait: epfd: {}, timeout: {:?}", epfd, timeout_msecs);
let mut proc = self.process();
let events = unsafe { self.vm().check_write_array(events, maxevents)? };
let epollInstance = proc.get_epoll_instance(epfd)?;
// add new fds which are registered by epoll_ctl after latest epoll_pwait
epollInstance.readyList.lock().clear();
epollInstance
.readyList
.lock()
.extend(epollInstance.newCtlList.lock().clone());
epollInstance.newCtlList.lock().clear();
// if registered fd has data to handle and its mode isn't epollet, we need
// to add it to the list.
let keys: Vec<_> = epollInstance.events.keys().cloned().collect();
for (k, v) in epollInstance.events.iter() {
if !v.contains(EpollEvent::EPOLLET) {
match &proc.files.get(k) {
None => {
// return Err(SysError::EINVAL);
}
Some(file_like) => {
let status = file_like.poll()?;
if status.write || status.read || status.error {
let mut readylist = epollInstance.readyList.lock();
readylist.insert(*k);
}
}
}
}
}
drop(proc);
let mut callbacks = alloc::vec![];
for fd in &keys {
let mut proc = self.process();
match proc.files.get(&fd) {
Some(file_like) => {
match file_like {
FileLike::File(file) => {
&crate::fs::STDIN.pushed.register_epoll_list(
self.thread.proc.clone(),
thread::current().id(),
epfd,
*fd,
);
callbacks.push((0, thread::current().id(), epfd, *fd));
}
FileLike::Socket(socket) => {
&(*crate::drivers::SOCKET_ACTIVITY).register_epoll_list(
self.thread.proc.clone(),
thread::current().id(),
epfd,
*fd,
);
callbacks.push((1, thread::current().id(), epfd, *fd));
}
FileLike::EpollInstance(instance) => {
return Err(SysError::EINVAL);
}
};
}
None => {}
}
drop(proc);
}
let mut condvars = alloc::vec![&(*TICK_ACTIVITY), &STDIN.pushed, &(*SOCKET_ACTIVITY)];
let begin_time_ms = crate::trap::uptime_msec();
let condition = move || {
use PollEvents as PE;
let mut proc = self.process();
let epollInstance = match proc.get_epoll_instance_mut(epfd) {
Ok(ins) => ins,
Err(err) => {
return Some(Err(err));
}
};
let readylist = epollInstance.readyList.lock().clone();
let mut events_num = 0;
for infd in readylist.iter() {
let mut status: PollStatus = Default::default();
{
if let Some(file_like) = proc.files.get(&infd) {
let _status = match file_like.poll() {
Ok(ret) => ret,
Err(err) => return Some(Err(err)),
};
status.write = _status.write;
status.read = _status.read;
status.error = _status.error;
}
}
{
let epollInstance = match proc.get_epoll_instance_mut(epfd) {
Ok(ins) => ins,
Err(err) => {
return Some(Err(err));
}
};
let epollevent = epollInstance.events.get_mut(&infd)?;
if status.error {
events[events_num].events = EpollEvent::EPOLLERR;
events[events_num].data = epollevent.data;
events_num += 1;
}
if status.read && epollevent.contains(EpollEvent::EPOLLIN) {
events[events_num].events = EpollEvent::EPOLLIN;
events[events_num].data = epollevent.data;
events_num += 1;
}
if status.write && epollevent.contains(EpollEvent::EPOLLOUT) {
events[events_num].events = EpollEvent::EPOLLOUT;
events[events_num].data = epollevent.data;
events_num += 1;
}
}
}
{
let epollInstance = match proc.get_epoll_instance_mut(epfd) {
Ok(ins) => ins,
Err(err) => {
return Some(Err(err));
}
};
epollInstance.readyList.lock().clear();
}
drop(proc);
// some event happens, so evoke the process
if events_num > 0 {
return Some(Ok(events_num));
}
let current_time_ms = crate::trap::uptime_msec();
// time runs out, so the evoke the process
if timeout_msecs < (1 << 31) && current_time_ms - begin_time_ms > timeout_msecs {
return Some(Ok(0));
}
return None;
};
let num = Condvar::wait_events(condvars.as_slice(), condition).unwrap();
for cb in callbacks.iter() {
match cb.0 {
0 => &crate::fs::STDIN
.pushed
.unregister_epoll_list(cb.1, cb.2, cb.3),
1 => &(*crate::drivers::SOCKET_ACTIVITY).unregister_epoll_list(cb.1, cb.2, cb.3),
_ => panic!("cb error"),
};
}
Ok(num)
}
pub fn sys_readv(&mut self, fd: usize, iov_ptr: *const IoVec, iov_count: usize) -> SysResult {
info!(
"readv: fd: {}, iov: {:?}, count: {}",

View File

@ -10,6 +10,7 @@ use rcore_memory::VMError;
use crate::arch::cpu;
use crate::arch::interrupt::TrapFrame;
use crate::arch::syscall::*;
use crate::fs::epoll::EpollEvent;
use crate::memory::{copy_from_user, MemorySet};
use crate::process::*;
use crate::sync::{Condvar, MutexGuard, SpinNoIrq};
@ -165,8 +166,20 @@ impl Syscall<'_> {
SYS_PPOLL => {
self.sys_ppoll(args[0] as *mut PollFd, args[1], args[2] as *const TimeSpec)
} // ignore sigmask
SYS_EPOLL_CREATE1 => self.unimplemented("epoll_create1", Err(SysError::ENOSYS)),
SYS_EPOLL_CREATE1 => self.sys_epoll_create1(args[0]),
SYS_EPOLL_CTL => {
self.sys_epoll_ctl(args[0], args[1], args[2], args[3] as *mut EpollEvent)
}
SYS_EPOLL_PWAIT => self.sys_epoll_pwait(
args[0],
args[1] as *mut EpollEvent,
args[2],
args[3],
args[4],
),
SYS_EVENTFD2 => self.unimplemented("eventfd2", Err(SysError::EACCES)),
SYS_SOCKETPAIR => self.unimplemented("socketpair", Err(SysError::EACCES)),
// file system
SYS_STATFS => self.unimplemented("statfs", Err(SysError::EACCES)),
SYS_FSTATFS => self.unimplemented("fstatfs", Err(SysError::EACCES)),
@ -422,6 +435,11 @@ impl Syscall<'_> {
3 => self.sys_semctl(args[1], args[2], args[3], args[4] as isize),
_ => return None,
},
SYS_EPOLL_CREATE => self.sys_epoll_create(args[0]),
SYS_EPOLL_WAIT => {
self.sys_epoll_wait(args[0], args[1] as *mut EpollEvent, args[2], args[3])
}
_ => return None,
};
Some(ret)
@ -457,7 +475,10 @@ impl Syscall<'_> {
SYS_CHOWN => self.unimplemented("chown", Ok(0)),
SYS_ARCH_PRCTL => self.sys_arch_prctl(args[0] as i32, args[1]),
SYS_TIME => self.sys_time(args[0] as *mut u64),
SYS_EPOLL_CREATE => self.unimplemented("epoll_create", Err(SysError::ENOSYS)),
SYS_EPOLL_CREATE => self.sys_epoll_create(args[0]),
SYS_EPOLL_WAIT => {
self.sys_epoll_wait(args[0], args[1] as *mut EpollEvent, args[2], args[3])
}
_ => return None,
};
Some(ret)

View File

@ -1,18 +1,27 @@
use crate::arch::cpu;
use crate::arch::interrupt::TrapFrame;
use crate::consts::INFORM_PER_MSEC;
use crate::process::*;
use crate::sync::Condvar;
use log::*;
pub static mut TICK: usize = 0;
lazy_static! {
pub static ref TICK_ACTIVITY: Condvar = Condvar::new();
}
pub fn uptime_msec() -> usize {
unsafe { crate::trap::TICK / crate::consts::USEC_PER_TICK / 1000 }
unsafe { crate::trap::TICK * crate::consts::USEC_PER_TICK / 1000 }
}
pub fn timer() {
if cpu::id() == 0 {
unsafe {
TICK += 1;
if uptime_msec() % INFORM_PER_MSEC == 0 {
TICK_ACTIVITY.notify_all();
}
}
}
processor().tick();

2
user

@ -1 +1 @@
Subproject commit 3ae3a840bf39bbd6628892842688c8720273546d
Subproject commit 653effaf38bfecedf38eba350ac8a589b80ea2d3