Browse Source

feat(fs): add eventfd syscall support (#858)

* feat(fs): add eventfd syscall support
linfeng 7 months ago
parent
commit
86ee1395de

+ 229 - 0
kernel/src/filesystem/eventfd.rs

@@ -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 - 0
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;

+ 10 - 0
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),
         };
 

+ 1 - 0
user/apps/test_eventfd/.gitignore

@@ -0,0 +1 @@
+test_eventfd

+ 20 - 0
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:

+ 52 - 0
user/apps/test_eventfd/main.c

@@ -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 - 0
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"]
+}