From 9ef7b5d0c7f3cacb04e7e02bd93b7888554ded52 Mon Sep 17 00:00:00 2001 From: Godones <1925466036@qq.com> Date: Sun, 21 Jul 2024 21:07:46 +0800 Subject: [PATCH 1/3] feat(fs): add eventfd syscall support --- kernel/src/filesystem/eventfd.rs | 191 +++++++++++++++++++++++ kernel/src/filesystem/mod.rs | 1 + kernel/src/syscall/mod.rs | 10 ++ user/apps/test_eventfd/.gitignore | 1 + user/apps/test_eventfd/Makefile | 20 +++ user/apps/test_eventfd/main.c | 52 ++++++ user/dadk/config/test_eventfd_0_1_0.dadk | 23 +++ 7 files changed, 298 insertions(+) create mode 100644 kernel/src/filesystem/eventfd.rs create mode 100644 user/apps/test_eventfd/.gitignore create mode 100644 user/apps/test_eventfd/Makefile create mode 100644 user/apps/test_eventfd/main.c create mode 100644 user/dadk/config/test_eventfd_0_1_0.dadk diff --git a/kernel/src/filesystem/eventfd.rs b/kernel/src/filesystem/eventfd.rs new file mode 100644 index 000000000..82d8063b7 --- /dev/null +++ b/kernel/src/filesystem/eventfd.rs @@ -0,0 +1,191 @@ +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); + +bitflags! { + pub struct EventFdFlags: u32{ + const EFD_SEMAPHORE = 1; + const EFD_CLOEXEC = 2; + 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, + 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( + &self, + _data: SpinLockGuard, + _mode: &FileMode, + ) -> Result<(), SystemError> { + Ok(()) + } + + fn close(&self, _data: SpinLockGuard) -> Result<(), SystemError> { + Ok(()) + } + + fn read_at( + &self, + _offset: usize, + len: usize, + buf: &mut [u8], + _data: SpinLockGuard, + ) -> Result { + 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); + } + + fn write_at( + &self, + _offset: usize, + len: usize, + buf: &[u8], + _data: SpinLockGuard, + ) -> Result { + 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); + } + + fn poll(&self, _private_data: &FilePrivateData) -> Result { + 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 { + 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 { + panic!("EventFd does not have a filesystem") + } + fn as_any_ref(&self) -> &dyn Any { + self + } + fn list(&self) -> Result, SystemError> { + Err(SystemError::EINVAL) + } +} + +impl Syscall { + pub fn sys_eventfd(init_val: u32, flags: u32) -> Result { + 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; + } +} diff --git a/kernel/src/filesystem/mod.rs b/kernel/src/filesystem/mod.rs index 3af494b0b..90dcc51bf 100644 --- a/kernel/src/filesystem/mod.rs +++ b/kernel/src/filesystem/mod.rs @@ -1,5 +1,6 @@ pub mod devfs; pub mod devpts; +pub mod eventfd; pub mod fat; pub mod kernfs; pub mod mbr; diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index 354e38f9f..cca260a03 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -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), }; diff --git a/user/apps/test_eventfd/.gitignore b/user/apps/test_eventfd/.gitignore new file mode 100644 index 000000000..fdf3c0f82 --- /dev/null +++ b/user/apps/test_eventfd/.gitignore @@ -0,0 +1 @@ +test_eventfd \ No newline at end of file diff --git a/user/apps/test_eventfd/Makefile b/user/apps/test_eventfd/Makefile new file mode 100644 index 000000000..fc61e3624 --- /dev/null +++ b/user/apps/test_eventfd/Makefile @@ -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: diff --git a/user/apps/test_eventfd/main.c b/user/apps/test_eventfd/main.c new file mode 100644 index 000000000..cea0dc0a0 --- /dev/null +++ b/user/apps/test_eventfd/main.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include +#include +#include + +int +main(int argc, char *argv[]) +{ + int efd; + uint64_t u; + ssize_t s; + + if (argc < 2) { + fprintf(stderr, "Usage: %s ...\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"); + } +} \ No newline at end of file diff --git a/user/dadk/config/test_eventfd_0_1_0.dadk b/user/dadk/config/test_eventfd_0_1_0.dadk new file mode 100644 index 000000000..ddd8f1f5e --- /dev/null +++ b/user/dadk/config/test_eventfd_0_1_0.dadk @@ -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"] +} \ No newline at end of file From 8fe8b8ed4f1381c6c2b909d0549aa696f0d83582 Mon Sep 17 00:00:00 2001 From: Godones <1925466036@qq.com> Date: Tue, 23 Jul 2024 23:45:03 +0800 Subject: [PATCH 2/3] add some docs --- kernel/src/filesystem/eventfd.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/kernel/src/filesystem/eventfd.rs b/kernel/src/filesystem/eventfd.rs index 82d8063b7..458f2200f 100644 --- a/kernel/src/filesystem/eventfd.rs +++ b/kernel/src/filesystem/eventfd.rs @@ -17,8 +17,15 @@ static EVENTFD_ID: AtomicU32 = AtomicU32::new(0); 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; } } @@ -65,6 +72,12 @@ impl IndexNode for EventFdInode { Ok(()) } + /// 1. counter !=0 + /// - 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, @@ -103,6 +116,13 @@ impl IndexNode for EventFdInode { return Ok(8); } + /// - 把一个 8 字节的int值写入到 counter 里 + /// - counter 最大值是 2^64 - 1 + /// - 如果写入时会发生溢出,则write会被阻塞 + /// - 如果 EFD_NONBLOCK 被设置,那么以 EAGAIN 失败 + /// - 以不合法的值写入时,会以 EINVAL 失败 + /// - 比如 0xffffffffffffffff 不合法 + /// - 比如 写入的值 size 小于8字节 fn write_at( &self, _offset: usize, @@ -137,6 +157,8 @@ impl IndexNode for EventFdInode { return Ok(8); } + /// - 如果 counter 的值大于 0 ,那么 fd 的状态就是可读的 + /// - 如果能无阻塞地写入一个至少为 1 的值,那么 fd 的状态就是可写的 fn poll(&self, _private_data: &FilePrivateData) -> Result { let mut events = EPollEventType::empty(); if self.eventfd.lock().count != 0 { @@ -172,6 +194,7 @@ impl IndexNode for EventFdInode { } impl Syscall { + /// See: https://man7.org/linux/man-pages/man2/eventfd2.2.html pub fn sys_eventfd(init_val: u32, flags: u32) -> Result { let flags = EventFdFlags::from_bits(flags).ok_or(SystemError::EINVAL)?; let id = EVENTFD_ID.fetch_add(1, core::sync::atomic::Ordering::Relaxed); From 307d165e95e235d48b9ed58cf5dae7ddd2e01911 Mon Sep 17 00:00:00 2001 From: Godones <1925466036@qq.com> Date: Wed, 24 Jul 2024 10:23:04 +0800 Subject: [PATCH 3/3] Update the format of comments --- kernel/src/filesystem/eventfd.rs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/kernel/src/filesystem/eventfd.rs b/kernel/src/filesystem/eventfd.rs index 458f2200f..038770034 100644 --- a/kernel/src/filesystem/eventfd.rs +++ b/kernel/src/filesystem/eventfd.rs @@ -10,10 +10,10 @@ use alloc::string::String; use alloc::sync::Arc; use alloc::vec::Vec; use core::any::Any; -use core::sync::atomic::AtomicU32; +use ida::IdAllocator; use system_error::SystemError; -static EVENTFD_ID: AtomicU32 = AtomicU32::new(0); +static EVENTFD_ID_ALLOCATOR: IdAllocator = IdAllocator::new(0, u32::MAX as usize); bitflags! { pub struct EventFdFlags: u32{ @@ -72,6 +72,8 @@ impl IndexNode for EventFdInode { Ok(()) } + /// # 从 counter 里读取一个 8 字节的int值 + /// /// 1. counter !=0 /// - EFD_SEMAPHORE 如果没有被设置,从 eventfd read,会得到 counter,并将它归0 /// - EFD_SEMAPHORE 如果被设置,从 eventfd read,会得到值 1,并将 counter - 1 @@ -116,7 +118,8 @@ impl IndexNode for EventFdInode { return Ok(8); } - /// - 把一个 8 字节的int值写入到 counter 里 + /// # 把一个 8 字节的int值写入到 counter 里 + /// /// - counter 最大值是 2^64 - 1 /// - 如果写入时会发生溢出,则write会被阻塞 /// - 如果 EFD_NONBLOCK 被设置,那么以 EAGAIN 失败 @@ -157,6 +160,8 @@ impl IndexNode for EventFdInode { return Ok(8); } + /// # 检查 eventfd 的状态 + /// /// - 如果 counter 的值大于 0 ,那么 fd 的状态就是可读的 /// - 如果能无阻塞地写入一个至少为 1 的值,那么 fd 的状态就是可写的 fn poll(&self, _private_data: &FilePrivateData) -> Result { @@ -194,10 +199,20 @@ impl IndexNode for EventFdInode { } impl Syscall { + /// # 创建一个 eventfd 文件描述符 + /// + /// ## 参数 + /// - `init_val`: u32: eventfd 的初始值 + /// - `flags`: u32: eventfd 的标志 + /// + /// ## 返回值 + /// - `Ok(usize)`: 成功创建的文件描述符 + /// - `Err(SystemError)`: 创建失败 + /// /// See: https://man7.org/linux/man-pages/man2/eventfd2.2.html pub fn sys_eventfd(init_val: u32, flags: u32) -> Result { let flags = EventFdFlags::from_bits(flags).ok_or(SystemError::EINVAL)?; - let id = EVENTFD_ID.fetch_add(1, core::sync::atomic::Ordering::Relaxed); + let id = EVENTFD_ID_ALLOCATOR.alloc().ok_or(SystemError::ENOMEM)? as u32; 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) {