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

implement syscall eventfd #28

Merged
merged 2 commits into from
Apr 12, 2024
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
188 changes: 188 additions & 0 deletions ulib/axstarry/src/syscall_fs/ctype/eventfd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
use alloc::sync::Arc;
use axerrno::{AxError, AxResult};
use axfs::api::{FileIO, FileIOType, OpenFlags};
use axsync::Mutex;
use axtask::yield_now;
use bitflags::bitflags;

bitflags! {
// https://sites.uclouvain.be/SystInfo/usr/include/sys/eventfd.h.html
#[derive(Clone, Copy, Debug)]
pub struct EventFdFlag: u32 {
const EFD_SEMAPHORE = 0x1;
const EFD_NONBLOCK = 0x800;
const EFD_CLOEXEC = 0x80000;
}
}

// https://man7.org/linux/man-pages/man2/eventfd2.2.html
pub struct EventFd {
value: Arc<Mutex<u64>>,
flags: u32,
}

impl EventFd {
pub fn new(initval: u64, flags: u32) -> EventFd {
EventFd {
value: Arc::new(Mutex::new(initval)),
flags,
}
}

fn should_block(&self) -> bool {
self.flags & EventFdFlag::EFD_NONBLOCK.bits() == 0
}

fn has_semaphore_set(&self) -> bool {
self.flags & EventFdFlag::EFD_SEMAPHORE.bits() != 0
}
}

impl FileIO for EventFd {
fn read(&self, buf: &mut [u8]) -> AxResult<usize> {
let len: usize = core::mem::size_of::<u64>();
if buf.len() < len {
return Err(AxError::InvalidInput);
}

loop {
let mut value_guard = self.value.lock();
// If EFD_SEMAPHORE was not specified and the eventfd counter has a nonzero value, then a read returns 8 bytes containing that value,
// and the counter's value is reset to zero.
if !self.has_semaphore_set() && *value_guard != 0 {
buf[0..len].copy_from_slice(&value_guard.to_ne_bytes());
*value_guard = 0;
return Ok(len);
}

// If EFD_SEMAPHORE was specified and the eventfd counter has a nonzero value, then a read returns 8 bytes containing the value 1,
// and the counter's value is decremented by 1.
if self.has_semaphore_set() && *value_guard != 0 {
let result: u64 = 1;
buf[0..len].copy_from_slice(&result.to_ne_bytes());
let _ = value_guard.checked_add_signed(-1);
return Ok(len);
}

// If the eventfd counter is zero at the time of the call to read,
// then the call either blocks until the counter becomes nonzero (at which time, the read proceeds as described above)
// or fails with the error EAGAIN if the file descriptor has been made nonblocking.
if *value_guard != 0 {
buf[0..len].copy_from_slice(&value_guard.to_ne_bytes());
return Ok(len);
}

if self.should_block() {
drop(value_guard);
yield_now()
} else {
return Err(AxError::WouldBlock);
}
}
}

fn write(&self, buf: &[u8]) -> AxResult<usize> {
let len: usize = core::mem::size_of::<u64>();

// A write fails with the error EINVAL if the size of the supplied buffer is less than 8 bytes,
// or if an attempt is made to write the value 0xffffffffffffffff.
let val = u64::from_ne_bytes(buf[0..len].try_into().unwrap());
if buf.len() < 8 || val == u64::MAX {
return Err(AxError::InvalidInput);
}

loop {
let mut value_guard = self.value.lock();
// The maximum value that may be stored in the counter is the largest unsigned 64-bit value minus 1 (i.e., 0xfffffffffffffffe).
match value_guard.checked_add(val + 1) {
// no overflow
Some(_) => {
*value_guard += val;
return Ok(len);
}
// overflow
None => {
if self.should_block() {
drop(value_guard);
yield_now()
} else {
return Err(AxError::WouldBlock);
}
}
}
}
}

fn readable(&self) -> bool {
true
}

fn writable(&self) -> bool {
true
}

fn executable(&self) -> bool {
false
}

fn get_type(&self) -> FileIOType {
FileIOType::Other
}

// The file descriptor is readable if the counter has a value greater than 0
fn ready_to_read(&self) -> bool {
*self.value.lock() > 0
}

// The file descriptor is writable if it is possible to write a value of at least "1" without blocking.
fn ready_to_write(&self) -> bool {
*self.value.lock() < u64::MAX - 1
}

fn get_status(&self) -> OpenFlags {
let mut status = OpenFlags::RDWR;
if self.flags & EventFdFlag::EFD_NONBLOCK.bits() != 0 {
status &= OpenFlags::NON_BLOCK;
}
if self.flags & EventFdFlag::EFD_CLOEXEC.bits() != 0 {
status &= OpenFlags::CLOEXEC;
}

status
}
}

#[cfg(test)]
mod tests {
use super::EventFd;
use axfs::api::FileIO;

#[test]
fn test_read() {
let event_fd = EventFd::new(42, 0);
let event_fd_val = 0u64;
let len = event_fd.read(&mut event_fd_val.to_ne_bytes()).unwrap();

assert_eq!(42, event_fd_val);
assert_eq!(4, len);
}

#[test]
fn test_read_with_bad_input() {
let event_fd = EventFd::new(42, 0);
let event_fd_val = 0u32;
let result = event_fd.read(&mut event_fd_val.to_ne_bytes());
assert_eq!(Err(AxError::InvalidInput), result);
}

#[test]
fn test_write() {
let event_fd = EventFd::new(42, 0);
let val = 12u64;
event_fd.write(&val.to_ne_bytes()[0..core::mem::size_of::<u64>()]);

let event_fd_val = 0u64;
event_fd.read(&mut event_fd_val.to_ne_bytes()).unwrap();
assert_eq!(54, event_fd_val);
}
}
2 changes: 2 additions & 0 deletions ulib/axstarry/src/syscall_fs/ctype/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ pub mod pipe;
pub use file::FileDesc;

pub mod epoll;

pub mod eventfd;
2 changes: 2 additions & 0 deletions ulib/axstarry/src/syscall_fs/fs_syscall_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ numeric_enum_macro::numeric_enum! {
pub enum FsSyscallId {
// fs
GETCWD = 17,
EVENT_FD = 19,
binary-bruce marked this conversation as resolved.
Show resolved Hide resolved
EPOLL_CREATE = 20,
EPOLL_CTL = 21,
EPOLL_WAIT = 22,
Expand Down Expand Up @@ -68,6 +69,7 @@ numeric_enum_macro::numeric_enum! {
// fs
OPEN = 2,
STAT = 4,
EVENT_FD = 284,
GETCWD = 79,
UNLINK = 87,
EPOLL_CREATE = 213,
Expand Down
25 changes: 25 additions & 0 deletions ulib/axstarry/src/syscall_fs/imp/eventfd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use alloc::sync::Arc;
use axprocess::current_process;

use crate::syscall_fs::ctype::eventfd::EventFd;
use crate::{SyscallError, SyscallResult};

pub fn syscall_eventfd(args: [usize; 6]) -> SyscallResult {
#[cfg(target_arch = "x86_64")]
unimplemented!("syscall eventfd is not tested in x86_64 yet");

let initval = args[0] as u64;
let flags = args[1] as u32;

let process = current_process();
let mut fd_table = process.fd_manager.fd_table.lock();
let fd_num = if let Ok(fd) = process.alloc_fd(&mut fd_table) {
fd
} else {
return Err(SyscallError::EPERM);
};

fd_table[fd_num] = Some(Arc::new(EventFd::new(initval, flags)));

Ok(fd_num as isize)
}
3 changes: 3 additions & 0 deletions ulib/axstarry/src/syscall_fs/imp/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ pub fn syscall_read(args: [usize; 6]) -> SyscallResult {
match file.read(buf) {
Ok(len) => Ok(len as isize),
Err(AxError::WouldBlock) => Err(SyscallError::EAGAIN),
Err(AxError::InvalidInput) => Err(SyscallError::EINVAL),
Err(_) => Err(SyscallError::EPERM),
}
}
Expand Down Expand Up @@ -155,6 +156,8 @@ pub fn syscall_write(args: [usize; 6]) -> SyscallResult {
// socket with send half closed
// TODO: send a SIGPIPE signal to the process
Err(axerrno::AxError::ConnectionReset) => Err(SyscallError::EPIPE),
Err(AxError::WouldBlock) => Err(SyscallError::EAGAIN),
Err(AxError::InvalidInput) => Err(SyscallError::EINVAL),
Err(_) => Err(SyscallError::EPERM),
}
}
Expand Down
2 changes: 2 additions & 0 deletions ulib/axstarry/src/syscall_fs/imp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ extern crate alloc;

mod ctl;
mod epoll;
mod eventfd;
mod io;
mod link;
mod mount;
mod poll;
mod stat;
pub use ctl::*;
pub use epoll::*;
pub use eventfd::*;
pub use io::*;
pub use link::*;
pub use mount::*;
Expand Down
1 change: 1 addition & 0 deletions ulib/axstarry/src/syscall_fs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use imp::*;
/// 文件系统相关系统调用
pub fn fs_syscall(syscall_id: fs_syscall_id::FsSyscallId, args: [usize; 6]) -> SyscallResult {
match syscall_id {
EVENT_FD => syscall_eventfd(args),
OPENAT => syscall_openat(args),
CLOSE => syscall_close(args),
READ => syscall_read(args),
Expand Down
Loading