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

feat(fs): add eventfd syscall support #858

Merged
merged 3 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
214 changes: 214 additions & 0 deletions kernel/src/filesystem/eventfd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
use crate::filesystem::vfs::file::{File, FileMode};
use crate::filesystem::vfs::syscall::ModeType;
use crate::filesystem::vfs::{FilePrivateData, FileSystem, FileType, IndexNode, Metadata};
use crate::libs::spinlock::{SpinLock, SpinLockGuard};
use crate::libs::wait_queue::WaitQueue;
use crate::net::event_poll::EPollEventType;
use crate::process::ProcessManager;
use crate::syscall::Syscall;
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::any::Any;
use core::sync::atomic::AtomicU32;
use system_error::SystemError;

static EVENTFD_ID: AtomicU32 = AtomicU32::new(0);
Samuka007 marked this conversation as resolved.
Show resolved Hide resolved

bitflags! {
pub struct EventFdFlags: u32{
/// Provide semaphore-like semantics for reads from the new
/// file descriptor.
const EFD_SEMAPHORE = 1;
/// Set the close-on-exec (FD_CLOEXEC) flag on the new file
/// descriptor
const EFD_CLOEXEC = 2;
/// Set the O_NONBLOCK file status flag on the open file
/// description (see open(2)) referred to by the new file
/// descriptor
const EFD_NONBLOCK = 4;
}
}

#[derive(Debug)]
pub struct EventFd {
count: u64,
flags: EventFdFlags,
#[allow(unused)]
id: u32,
}

impl EventFd {
pub fn new(count: u64, flags: EventFdFlags, id: u32) -> Self {
EventFd { count, flags, id }
}
}

#[derive(Debug)]
pub struct EventFdInode {
eventfd: SpinLock<EventFd>,
wait_queue: WaitQueue,
}

impl EventFdInode {
pub fn new(eventfd: EventFd) -> Self {
EventFdInode {
eventfd: SpinLock::new(eventfd),
wait_queue: WaitQueue::default(),
}
}
}

impl IndexNode for EventFdInode {
fn open(
Samuka007 marked this conversation as resolved.
Show resolved Hide resolved
&self,
_data: SpinLockGuard<FilePrivateData>,
_mode: &FileMode,
) -> Result<(), SystemError> {
Ok(())
}

fn close(&self, _data: SpinLockGuard<FilePrivateData>) -> Result<(), SystemError> {
Ok(())
}

/// 1. counter !=0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

注释?

/// - EFD_SEMAPHORE 如果没有被设置,从 eventfd read,会得到 counter,并将它归0
/// - EFD_SEMAPHORE 如果被设置,从 eventfd read,会得到值 1,并将 counter - 1
/// 2. counter == 0
/// - EFD_NONBLOCK 如果被设置,那么会以 EAGAIN 的错失败
/// - 否则 read 会被阻塞,直到为非0。
fn read_at(
&self,
_offset: usize,
len: usize,
buf: &mut [u8],
_data: SpinLockGuard<FilePrivateData>,
) -> Result<usize, SystemError> {
if len < 8 {
return Err(SystemError::EINVAL);
}
let mut val = loop {
let val = self.eventfd.lock().count;
if val != 0 {
break val;
}
if self
.eventfd
.lock()
.flags
.contains(EventFdFlags::EFD_NONBLOCK)
{
return Err(SystemError::EAGAIN_OR_EWOULDBLOCK);
}
self.wait_queue.sleep();
};

let mut eventfd = self.eventfd.lock();
if eventfd.flags.contains(EventFdFlags::EFD_SEMAPHORE) {
eventfd.count -= 1;
val = 1;
} else {
eventfd.count = 0;
}
let val_bytes = val.to_ne_bytes();
buf[..8].copy_from_slice(&val_bytes);
return Ok(8);
}

/// - 把一个 8 字节的int值写入到 counter 里
/// - counter 最大值是 2^64 - 1
/// - 如果写入时会发生溢出,则write会被阻塞
/// - 如果 EFD_NONBLOCK 被设置,那么以 EAGAIN 失败
/// - 以不合法的值写入时,会以 EINVAL 失败
/// - 比如 0xffffffffffffffff 不合法
/// - 比如 写入的值 size 小于8字节
fn write_at(
&self,
_offset: usize,
len: usize,
buf: &[u8],
_data: SpinLockGuard<FilePrivateData>,
) -> Result<usize, SystemError> {
if len < 8 {
return Err(SystemError::EINVAL);
}
let val = u64::from_ne_bytes(buf[..8].try_into().unwrap());
if val == u64::MAX {
return Err(SystemError::EINVAL);
}
loop {
let eventfd = self.eventfd.lock();
if u64::MAX - eventfd.count > val {
break;
}
// block until a read() is performed on the
// file descriptor, or fails with the error EAGAIN if the
// file descriptor has been made nonblocking.
if eventfd.flags.contains(EventFdFlags::EFD_NONBLOCK) {
return Err(SystemError::EAGAIN_OR_EWOULDBLOCK);
}
drop(eventfd);
self.wait_queue.sleep();
}
let mut eventfd = self.eventfd.lock();
eventfd.count += val;
self.wait_queue.wakeup_all(None);
return Ok(8);
}

/// - 如果 counter 的值大于 0 ,那么 fd 的状态就是可读的
/// - 如果能无阻塞地写入一个至少为 1 的值,那么 fd 的状态就是可写的
fn poll(&self, _private_data: &FilePrivateData) -> Result<usize, SystemError> {
let mut events = EPollEventType::empty();
if self.eventfd.lock().count != 0 {
events |= EPollEventType::EPOLLIN | EPollEventType::EPOLLRDNORM;
}
if self.eventfd.lock().count != u64::MAX {
events |= EPollEventType::EPOLLOUT | EPollEventType::EPOLLWRNORM;
}
return Ok(events.bits() as usize);
}

fn metadata(&self) -> Result<Metadata, SystemError> {
let meta = Metadata {
mode: ModeType::from_bits_truncate(0o755),
file_type: FileType::File,
..Default::default()
};
Ok(meta)
}

fn resize(&self, _len: usize) -> Result<(), SystemError> {
Ok(())
}
fn fs(&self) -> Arc<dyn FileSystem> {
panic!("EventFd does not have a filesystem")
}
fn as_any_ref(&self) -> &dyn Any {
self
}
fn list(&self) -> Result<Vec<String>, SystemError> {
Err(SystemError::EINVAL)
}
}

impl Syscall {
/// See: https://man7.org/linux/man-pages/man2/eventfd2.2.html
pub fn sys_eventfd(init_val: u32, flags: u32) -> Result<usize, SystemError> {
let flags = EventFdFlags::from_bits(flags).ok_or(SystemError::EINVAL)?;
let id = EVENTFD_ID.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
let eventfd = EventFd::new(init_val as u64, flags, id);
let inode = Arc::new(EventFdInode::new(eventfd));
let filemode = if flags.contains(EventFdFlags::EFD_CLOEXEC) {
FileMode::O_RDWR | FileMode::O_CLOEXEC
} else {
FileMode::O_RDWR
};
let file = File::new(inode, filemode)?;
let binding = ProcessManager::current_pcb().fd_table();
let mut fd_table_guard = binding.write();
let fd = fd_table_guard.alloc_fd(file, None).map(|x| x as usize);
return fd;
}
}
1 change: 1 addition & 0 deletions kernel/src/filesystem/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod devfs;
pub mod devpts;
pub mod eventfd;
pub mod fat;
pub mod kernfs;
pub mod mbr;
Expand Down
10 changes: 10 additions & 0 deletions kernel/src/syscall/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1121,6 +1121,16 @@ impl Syscall {
}
#[cfg(target_arch = "x86_64")]
SYS_UTIMES => Self::sys_utimes(args[0] as *const u8, args[1] as *const PosixTimeval),
#[cfg(target_arch = "x86_64")]
SYS_EVENTFD => {
let initval = args[0] as u32;
Self::sys_eventfd(initval, 0)
}
SYS_EVENTFD2 => {
let initval = args[0] as u32;
let flags = args[1] as u32;
Self::sys_eventfd(initval, flags)
}
_ => panic!("Unsupported syscall ID: {}", syscall_num),
};

Expand Down
1 change: 1 addition & 0 deletions user/apps/test_eventfd/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test_eventfd
20 changes: 20 additions & 0 deletions user/apps/test_eventfd/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
ifeq ($(ARCH), x86_64)
CROSS_COMPILE=x86_64-linux-musl-
else ifeq ($(ARCH), riscv64)
CROSS_COMPILE=riscv64-linux-musl-
endif

CC=$(CROSS_COMPILE)gcc

.PHONY: all
all: main.c
$(CC) -static -o test_eventfd main.c

.PHONY: install clean
install: all
mv test_eventfd $(DADK_CURRENT_BUILD_DIR)/test_eventfd

clean:
rm test_eventfd *.o

fmt:
52 changes: 52 additions & 0 deletions user/apps/test_eventfd/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#include <err.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/eventfd.h>
#include <sys/types.h>
#include <unistd.h>

int
main(int argc, char *argv[])
{
int efd;
uint64_t u;
ssize_t s;

if (argc < 2) {
fprintf(stderr, "Usage: %s <num>...\n", argv[0]);
exit(EXIT_FAILURE);
}

efd = eventfd(0, 0);
if (efd == -1)
err(EXIT_FAILURE, "eventfd");

switch (fork()) {
case 0:
for (size_t j = 1; j < argc; j++) {
printf("Child writing %s to efd\n", argv[j]);
u = strtoull(argv[j], NULL, 0);
/* strtoull() allows various bases */
s = write(efd, &u, sizeof(uint64_t));
if (s != sizeof(uint64_t))
err(EXIT_FAILURE, "write");
}
printf("Child completed write loop\n");

exit(EXIT_SUCCESS);

default:
sleep(2);

printf("Parent about to read\n");
s = read(efd, &u, sizeof(uint64_t));
if (s != sizeof(uint64_t))
err(EXIT_FAILURE, "read");
printf("Parent read %"PRIu64" (%#"PRIx64") from efd\n", u, u);
exit(EXIT_SUCCESS);

case -1:
err(EXIT_FAILURE, "fork");
}
}
23 changes: 23 additions & 0 deletions user/dadk/config/test_eventfd_0_1_0.dadk
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "test_eventfd",
"version": "0.1.0",
"description": "test_eventfd",
"task_type": {
"BuildFromSource": {
"Local": {
"path": "apps/test_eventfd"
}
}
},
"depends": [],
"build": {
"build_command": "make install"
},
"install": {
"in_dragonos_path": "/bin"
},
"clean": {
"clean_command": "make clean"
},
"target_arch": ["x86_64"]
}
Loading