mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-08 14:16:47 +00:00
feat(fs): add eventfd syscall support (#858)
* feat(fs): add eventfd syscall support
This commit is contained in:
parent
bd70d2d1f4
commit
86ee1395de
229
kernel/src/filesystem/eventfd.rs
Normal file
229
kernel/src/filesystem/eventfd.rs
Normal file
@ -0,0 +1,229 @@
|
||||
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 ida::IdAllocator;
|
||||
use system_error::SystemError;
|
||||
|
||||
static EVENTFD_ID_ALLOCATOR: IdAllocator = IdAllocator::new(0, u32::MAX as usize);
|
||||
|
||||
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(
|
||||
&self,
|
||||
_data: SpinLockGuard<FilePrivateData>,
|
||||
_mode: &FileMode,
|
||||
) -> Result<(), SystemError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn close(&self, _data: SpinLockGuard<FilePrivateData>) -> Result<(), SystemError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// # 从 counter 里读取一个 8 字节的int值
|
||||
///
|
||||
/// 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,
|
||||
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);
|
||||
}
|
||||
|
||||
/// # 检查 eventfd 的状态
|
||||
///
|
||||
/// - 如果 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 {
|
||||
/// # 创建一个 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<usize, SystemError> {
|
||||
let flags = EventFdFlags::from_bits(flags).ok_or(SystemError::EINVAL)?;
|
||||
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) {
|
||||
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,5 +1,6 @@
|
||||
pub mod devfs;
|
||||
pub mod devpts;
|
||||
pub mod eventfd;
|
||||
pub mod fat;
|
||||
pub mod kernfs;
|
||||
pub mod mbr;
|
||||
|
@ -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),
|
||||
};
|
||||
|
||||
|
1
user/apps/test_eventfd/.gitignore
vendored
Normal file
1
user/apps/test_eventfd/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
test_eventfd
|
20
user/apps/test_eventfd/Makefile
Normal file
20
user/apps/test_eventfd/Makefile
Normal file
@ -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
user/apps/test_eventfd/main.c
Normal file
52
user/apps/test_eventfd/main.c
Normal file
@ -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
user/dadk/config/test_eventfd_0_1_0.dadk
Normal file
23
user/dadk/config/test_eventfd_0_1_0.dadk
Normal file
@ -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…
x
Reference in New Issue
Block a user