diff --git a/kernel/aster-nix/src/syscall/arch/x86.rs b/kernel/aster-nix/src/syscall/arch/x86.rs index fb7e0fdb1..293af2b9d 100644 --- a/kernel/aster-nix/src/syscall/arch/x86.rs +++ b/kernel/aster-nix/src/syscall/arch/x86.rs @@ -265,7 +265,7 @@ impl_syscall_nums_and_dispatch_fn! { SYS_FCHMODAT = 268 => sys_fchmodat(args[..3]); SYS_SET_ROBUST_LIST = 273 => sys_set_robust_list(args[..2]); SYS_UTIMENSAT = 280 => sys_utimensat(args[..4]); - SYS_EPOLL_PWAIT = 281 => sys_epoll_pwait(args[..5]); + SYS_EPOLL_PWAIT = 281 => sys_epoll_pwait(args[..6]); SYS_EVENTFD = 284 => sys_eventfd(args[..1]); SYS_ACCEPT4 = 288 => sys_accept4(args[..4]); SYS_EVENTFD2 = 290 => sys_eventfd2(args[..2]); diff --git a/kernel/aster-nix/src/syscall/epoll.rs b/kernel/aster-nix/src/syscall/epoll.rs index 481e68345..8ea5f788b 100644 --- a/kernel/aster-nix/src/syscall/epoll.rs +++ b/kernel/aster-nix/src/syscall/epoll.rs @@ -11,6 +11,7 @@ use crate::{ utils::CreationFlags, }, prelude::*, + process::posix_thread::PosixThreadExt, util::{read_val_from_user, write_val_to_user}, }; @@ -89,12 +90,7 @@ pub fn sys_epoll_ctl( Ok(SyscallReturn::Return(0 as _)) } -pub fn sys_epoll_wait( - epfd: FileDesc, - events_addr: Vaddr, - max_events: i32, - timeout: i32, -) -> Result { +fn do_epoll_wait(epfd: FileDesc, max_events: i32, timeout: i32) -> Result> { let max_events = { if max_events <= 0 { return_errno_with_message!(Errno::EINVAL, "max_events is not positive"); @@ -106,20 +102,30 @@ pub fn sys_epoll_wait( } else { None }; + + let current = current!(); + let file_table = current.file_table().lock(); + let epoll_file = file_table + .get_file(epfd)? + .downcast_ref::() + .ok_or(Error::with_message(Errno::EINVAL, "not epoll file"))?; + let epoll_events = epoll_file.wait(max_events, timeout.as_ref())?; + + Ok(epoll_events) +} + +pub fn sys_epoll_wait( + epfd: FileDesc, + events_addr: Vaddr, + max_events: i32, + timeout: i32, +) -> Result { debug!( "epfd = {}, events_addr = 0x{:x}, max_events = {}, timeout = {:?}", epfd, events_addr, max_events, timeout ); - let current = current!(); - let file = { - let file_table = current.file_table().lock(); - file_table.get_file(epfd)?.clone() - }; - let epoll_file = file - .downcast_ref::() - .ok_or(Error::with_message(Errno::EINVAL, "not epoll file"))?; - let epoll_events = epoll_file.wait(max_events, timeout.as_ref())?; + let epoll_events = do_epoll_wait(epfd, max_events, timeout)?; // Write back let mut write_addr = events_addr; @@ -132,17 +138,74 @@ pub fn sys_epoll_wait( Ok(SyscallReturn::Return(epoll_events.len() as _)) } +fn set_signal_mask(set_ptr: Vaddr) -> Result { + let new_set: Option = if set_ptr != 0 { + Some(read_val_from_user::(set_ptr)?) + } else { + None + }; + + let current_thread = current_thread!(); + let posix_thread = current_thread.as_posix_thread().unwrap(); + let mut sig_mask = posix_thread.sig_mask().lock(); + + let old_sig_mask_value = sig_mask.as_u64(); + + if let Some(new_set) = new_set { + sig_mask.set(new_set); + } + + Ok(old_sig_mask_value) +} + +fn restore_signal_mask(sig_mask_val: u64) { + let current_thread = current_thread!(); + let posix_thread = current_thread.as_posix_thread().unwrap(); + let mut sig_mask = posix_thread.sig_mask().lock(); + + sig_mask.set(sig_mask_val); +} + pub fn sys_epoll_pwait( epfd: FileDesc, events_addr: Vaddr, max_events: i32, timeout: i32, - sigmask: Vaddr, //TODO: handle sigmask + sigmask: Vaddr, + sigset_size: usize, ) -> Result { - if sigmask != 0 { - warn!("epoll_pwait cannot handle signal mask, yet"); + debug!( + "epfd = {}, events_addr = 0x{:x}, max_events = {}, timeout = {:?}, sigmask = 0x{:x}, sigset_size = {}", + epfd, events_addr, max_events, timeout, sigmask, sigset_size + ); + + if sigset_size != 8 { + error!("sigset size is not equal to 8"); } - sys_epoll_wait(epfd, events_addr, max_events, timeout) + + let old_sig_mask_value = set_signal_mask(sigmask)?; + + let ready_events = match do_epoll_wait(epfd, max_events, timeout) { + Ok(events) => { + restore_signal_mask(old_sig_mask_value); + events + } + Err(e) => { + // Restore the signal mask even if an error occurs + restore_signal_mask(old_sig_mask_value); + return Err(e); + } + }; + + // Write back + let mut write_addr = events_addr; + for event in ready_events.iter() { + let c_event = c_epoll_event::from(event); + write_val_to_user(write_addr, &c_event)?; + write_addr += core::mem::size_of::(); + } + + Ok(SyscallReturn::Return(ready_events.len() as _)) } #[derive(Debug, Clone, Copy, Pod)] diff --git a/regression/apps/Makefile b/regression/apps/Makefile index 6427a5466..dfb60041e 100644 --- a/regression/apps/Makefile +++ b/regression/apps/Makefile @@ -14,6 +14,7 @@ TEST_APPS := \ capability \ clone3 \ cpu_affinity \ + epoll \ eventfd2 \ execve \ file_io \ diff --git a/regression/apps/epoll/Makefile b/regression/apps/epoll/Makefile new file mode 100644 index 000000000..ce42e33b0 --- /dev/null +++ b/regression/apps/epoll/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: MPL-2.0 + +include ../test_common.mk + +EXTRA_C_FLAGS := \ No newline at end of file diff --git a/regression/apps/epoll/epoll_pwait.c b/regression/apps/epoll/epoll_pwait.c new file mode 100644 index 000000000..f630c0c0a --- /dev/null +++ b/regression/apps/epoll/epoll_pwait.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include +#include + +// Signal handler for SIGUSR1 +static void handle_sigusr1(int sig) +{ + write(STDOUT_FILENO, "SIGUSR1 handled\n", 16); +} + +int main(void) +{ + int pipefd[2]; // Array to store pipe file descriptors + pid_t cpid; // Child process ID + char buf[1024]; // Read buffer + struct epoll_event ev, events[1]; + int epfd, nfds; + + // Create a pipe + if (pipe(pipefd) == -1) { + perror("pipe error"); + exit(EXIT_FAILURE); + } + + // Create epoll instance + if ((epfd = epoll_create1(0)) == -1) { + perror("epoll_create1 error"); + exit(EXIT_FAILURE); + } + + // Fork to create child process + cpid = fork(); + if (cpid == -1) { + perror("fork error"); + exit(EXIT_FAILURE); + } + + if (cpid == 0) { // Child process + close(pipefd[0]); // Child closes read end of the pipe + + sleep(3); // Sleep for several seconds to provide a time window to send SIGUSR1 + + const char *message = "Message from child process\n"; + write(pipefd[1], message, + strlen(message)); // Write a string to the pipe + close(pipefd[1]); // Close write end of the pipe + _exit(EXIT_SUCCESS); + } else { + // Parent process + struct sigaction sa; + sigset_t sigset; + + // Setup signal handler for SIGUSR1 + sa.sa_handler = handle_sigusr1; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + if (sigaction(SIGUSR1, &sa, NULL) == -1) { + perror("sigaction error"); + exit(EXIT_FAILURE); + } + + // Prepare the signal set to block SIGUSR1 + sigemptyset(&sigset); + sigaddset(&sigset, SIGUSR1); + + close(pipefd[1]); // Parent closes write end of the pipe + + // Set up epoll to listen for events + ev.events = EPOLLIN; // Listen for input events + ev.data.fd = pipefd[0]; + if (epoll_ctl(epfd, EPOLL_CTL_ADD, pipefd[0], &ev) == -1) { + perror("epoll_ctl error"); + exit(EXIT_FAILURE); + } + + // Wait for events to occur, blocking SIGUSR1 + printf("Waiting for event on pipe, SIGUSR1 is blocked...\n"); + nfds = epoll_pwait(epfd, events, 1, -1, &sigset); + if (nfds == -1) { + perror("epoll_pwait error"); + exit(EXIT_FAILURE); + } + + // If we get here, epoll_pwait was successful + printf("epoll_pwait returned successfully.\n"); + if (events[0].data.fd == pipefd[0]) { + // Read data + ssize_t count = read(pipefd[0], buf, sizeof(buf) - 1); + if (count > 0) { + buf[count] = + '\0'; // Ensure string is null-terminated + printf("Received data: %s", + buf); // Output the entire string + } + } + + close(pipefd[0]); // Close read end of the pipe + close(epfd); // Close the epoll file descriptor + } + + // Wait for the child process to complete + wait(NULL); + + return EXIT_SUCCESS; +} diff --git a/regression/apps/epoll/epoll_wait.c b/regression/apps/epoll/epoll_wait.c new file mode 100644 index 000000000..3c5a70f18 --- /dev/null +++ b/regression/apps/epoll/epoll_wait.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include + +int main(void) +{ + int pipefd[2]; // Array to store pipe file descriptors + pid_t cpid; // Child process ID + char buf[1024]; // Read buffer + struct epoll_event ev, events[1]; + int epfd, nfds; + + // Create a pipe + if (pipe(pipefd) == -1) { + perror("pipe error"); + return EXIT_FAILURE; + } + + // Create epoll instance + if ((epfd = epoll_create1(0)) == -1) { + perror("epoll_create1 error"); + close(pipefd[0]); + close(pipefd[1]); + return EXIT_FAILURE; + } + + // Fork to create child process + cpid = fork(); + if (cpid == -1) { + perror("fork error"); + close(pipefd[0]); + close(pipefd[1]); + close(epfd); + return EXIT_FAILURE; + } + + if (cpid == 0) { // Child process + close(pipefd[0]); // Child closes read end of the pipe + const char *message = "Hello, world!\n"; + write(pipefd[1], message, + strlen(message)); // Write a string to the pipe + close(pipefd[1]); // Close write end of the pipe + _exit(EXIT_SUCCESS); + } else { // Parent process + close(pipefd[1]); // Parent closes write end of the pipe + + // Set up epoll to listen for events + ev.events = EPOLLIN; // Listen for input events + ev.data.fd = pipefd[0]; + if (epoll_ctl(epfd, EPOLL_CTL_ADD, pipefd[0], &ev) == -1) { + perror("epoll_ctl error"); + close(pipefd[0]); + close(epfd); + return EXIT_FAILURE; + } + + // Wait for events to occur + printf("Waiting for event on pipe...\n"); + nfds = epoll_wait(epfd, events, 1, -1); + if (nfds == -1) { + perror("epoll_wait error"); + close(pipefd[0]); + close(epfd); + return EXIT_FAILURE; + } + + // If we get here, epoll_wait was successful + printf("epoll_wait returned successfully.\n"); + if (events[0].data.fd == pipefd[0]) { + // Read data + ssize_t count = read(pipefd[0], buf, sizeof(buf) - 1); + if (count > 0) { + buf[count] = + '\0'; // Ensure string is null-terminated + printf("Received data: %s", + buf); // Output the entire string + } else { + perror("read error"); + } + } + + close(pipefd[0]); // Close read end of the pipe + close(epfd); // Close the epoll file descriptor + } + + // Wait for the child process to complete + wait(NULL); + + return EXIT_SUCCESS; +} diff --git a/regression/apps/scripts/run_regression_test.sh b/regression/apps/scripts/run_regression_test.sh index 1382119b7..b07b234ee 100755 --- a/regression/apps/scripts/run_regression_test.sh +++ b/regression/apps/scripts/run_regression_test.sh @@ -11,5 +11,6 @@ cd ${SCRIPT_DIR} ./ext2.sh ./process.sh ./network.sh +./test_epoll_pwait.sh echo "All regression tests passed." diff --git a/regression/apps/scripts/test_epoll_pwait.sh b/regression/apps/scripts/test_epoll_pwait.sh new file mode 100755 index 000000000..2de6e092d --- /dev/null +++ b/regression/apps/scripts/test_epoll_pwait.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +# SPDX-License-Identifier: MPL-2.0 + +set -e + +EPOLLTEST_DIR=/regression/epoll +cd ${EPOLLTEST_DIR} + +echo "Start epoll_pwait test......" + +# Step 2: Run epoll_pwait in the background +./epoll_pwait & +EPOLL_PID=$! + +echo "epoll_pwait PID: $EPOLL_PID" + +# Step 3: Wait for 1 seconds to let epoll_pwait initialize and block SIGUSR1 +sleep 1 + +# Step 4: Send SIGUSR1 to epoll_pwait +kill -USR1 $EPOLL_PID +echo "Sent SIGUSR1 to PID $EPOLL_PID" + +# Optional: Wait a bit more to see the output if the process is still running +sleep 3 + +# You can also wait till the subprocess epoll_pwait completely finishes +# wait $EPOLL_PID + +echo "Test completed." diff --git a/regression/syscall_test/blocklists/epoll_test b/regression/syscall_test/blocklists/epoll_test index 422562625..658c924d8 100644 --- a/regression/syscall_test/blocklists/epoll_test +++ b/regression/syscall_test/blocklists/epoll_test @@ -1,9 +1,4 @@ -EpollTest.AllWritable -EpollTest.LastReadable -EpollTest.LastNonWritable EpollTest.Timeout_NoRandomSave -EpollTest.WaitThenUnblock -EpollTest.UnblockWithSignal EpollTest.TimeoutNoFds EpollTest.UnblockWithNewFD EpollTest.Oneshot