forked from DragonOS-Community/DragonOS
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(fs): add eventfd syscall support (DragonOS-Community#858)
* feat(fs): add eventfd syscall support
- Loading branch information
Showing
7 changed files
with
336 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
test_eventfd |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"] | ||
} |