diff --git a/kernel/src/fs/epoll/epoll_file.rs b/kernel/src/fs/epoll/epoll_file.rs index ddfcbb061..365e6f22b 100644 --- a/kernel/src/fs/epoll/epoll_file.rs +++ b/kernel/src/fs/epoll/epoll_file.rs @@ -1,8 +1,5 @@ // SPDX-License-Identifier: MPL-2.0 -#![allow(dead_code)] -#![allow(unused_variables)] - use core::{ borrow::Borrow, sync::atomic::{AtomicBool, Ordering}, @@ -149,7 +146,7 @@ impl EpollFile { .ok_or_else(|| { Error::with_message(Errno::ENOENT, "fd is not in the interest list") })?; - entry.update(new_ep_event, new_ep_flags); + entry.update(new_ep_event, new_ep_flags)?; entry.clone() }; @@ -232,20 +229,22 @@ impl EpollFile { continue; }; - let (ep_event, ep_flags) = entry.event_and_flags(); - // If this entry's file is ready, save it in the output array. - // EPOLLHUP and EPOLLERR should always be reported. - let ready_events = entry.poll() & (ep_event.events | IoEvents::HUP | IoEvents::ERR); - - // Records the events from the ready list - if !ready_events.is_empty() { - ep_events.push(EpollEvent::new(ready_events, ep_event.user_data)); - count_events += 1; - } + let (has_ready_events, ep_flags) = match entry.poll() { + // If this entry's file is ready, the events need to be saved in the output array. + Some((ready_events, user_data, ep_flags)) if !ready_events.is_empty() => { + ep_events.push(EpollEvent::new(ready_events, user_data)); + count_events += 1; + (true, ep_flags) + } + // If the file is not ready, there is nothing to do. + Some((_, _, ep_flags)) => (false, ep_flags), + // If the file no longer exists, the entry should be removed. + None => (false, EpollFlags::ONE_SHOT), + }; // If there are events and the epoll entry is neither edge-triggered // nor one-shot, then we should keep the entry in the ready list. - if !ready_events.is_empty() + if has_ready_events && !ep_flags.intersects(EpollFlags::ONE_SHOT | EpollFlags::EDGE_TRIGGER) { ready.push_back(weak_entry); @@ -285,11 +284,11 @@ impl Pollable for EpollFile { // Implement the common methods required by FileHandle impl FileLike for EpollFile { - fn read(&self, writer: &mut VmWriter) -> Result { + fn read(&self, _writer: &mut VmWriter) -> Result { return_errno_with_message!(Errno::EINVAL, "epoll files do not support read"); } - fn write(&self, reader: &mut VmReader) -> Result { + fn write(&self, _reader: &mut VmReader) -> Result { return_errno_with_message!(Errno::EINVAL, "epoll files do not support write"); } @@ -407,38 +406,30 @@ impl EpollEntry { self.key.file.upgrade().map(KeyableArc::into) } - /// Get the epoll event associated with the epoll entry. - pub fn event(&self) -> EpollEvent { - let inner = self.inner.lock(); - inner.event - } - - /// Get the epoll flags associated with the epoll entry. - pub fn flags(&self) -> EpollFlags { - let inner = self.inner.lock(); - inner.flags - } - - /// Get the epoll event and flags that are associated with this epoll entry. - pub fn event_and_flags(&self) -> (EpollEvent, EpollFlags) { - let inner = self.inner.lock(); - (inner.event, inner.flags) - } - /// Poll the events of the file associated with this epoll entry. /// /// If the returned events is not empty, then the file is considered ready. - pub fn poll(&self) -> IoEvents { - match self.file() { - Some(file) => file.poll(IoEvents::all(), None), - None => IoEvents::empty(), - } + pub fn poll(&self) -> Option<(IoEvents, u64, EpollFlags)> { + let file = self.file()?; + + let (event, flags) = { + let inner = self.inner.lock(); + (inner.event, inner.flags) + }; + + Some((file.poll(event.events, None), event.user_data, flags)) } /// Update the epoll entry, most likely to be triggered via `EpollCtl::Mod`. - pub fn update(&self, event: EpollEvent, flags: EpollFlags) { + pub fn update(&self, event: EpollEvent, flags: EpollFlags) -> Result<()> { let mut inner = self.inner.lock(); - *inner = EpollEntryInner { event, flags } + + if let Some(file) = self.file() { + file.register_observer(self.self_weak(), event.events)?; + } + *inner = EpollEntryInner { event, flags }; + + Ok(()) } /// Returns whether the epoll entry is in the ready list. diff --git a/test/apps/epoll/epoll_err.c b/test/apps/epoll/epoll_err.c index fdcaad837..3aab1b0fb 100644 --- a/test/apps/epoll/epoll_err.c +++ b/test/apps/epoll/epoll_err.c @@ -50,3 +50,44 @@ FN_TEST(epoll_add_del) TEST_SUCC(close(rfd2)); } END_TEST() + +FN_TEST(epoll_mod) +{ + int fildes[2]; + int epfd, rfd, wfd; + struct epoll_event ev; + char buf[1]; + + // Setup pipes + TEST_SUCC(pipe(fildes)); + rfd = fildes[0]; + wfd = fildes[1]; + TEST_SUCC(write(wfd, "", 1)); + + // Setup epoll + epfd = TEST_SUCC(epoll_create1(0)); + ev.events = EPOLLOUT; + ev.data.fd = rfd; + TEST_SUCC(epoll_ctl(epfd, EPOLL_CTL_ADD, rfd, &ev)); + + // Wait for EPOLLOUT + TEST_RES(epoll_wait(epfd, &ev, 1, 0), _ret == 0); + + // Modify the events + ev.events = EPOLLIN; + ev.data.fd = rfd; + TEST_SUCC(epoll_ctl(epfd, EPOLL_CTL_MOD, rfd, &ev)); + + // Wait for EPOLLIN + TEST_RES(epoll_wait(epfd, &ev, 1, 0), _ret == 1); + TEST_SUCC(read(rfd, buf, 1)); + TEST_RES(epoll_wait(epfd, &ev, 1, 0), _ret == 0); + TEST_SUCC(write(wfd, "", 1)); + TEST_RES(epoll_wait(epfd, &ev, 1, 0), _ret == 1); + + // Clean up + TEST_SUCC(close(epfd)); + TEST_SUCC(close(rfd)); + TEST_SUCC(close(wfd)); +} +END_TEST()