Don't allocate when possible in do_poll

This commit is contained in:
Ruihan Li
2025-01-11 00:47:12 +08:00
committed by Tate, Hongliang Tian
parent 185b27b01c
commit a6b3a65fe5

View File

@ -1,6 +1,5 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use alloc::borrow::Cow;
use core::{cell::Cell, time::Duration}; use core::{cell::Cell, time::Duration};
use super::SyscallReturn; use super::SyscallReturn;
@ -61,28 +60,17 @@ pub fn sys_poll(fds: Vaddr, nfds: u64, timeout: i32, ctx: &Context) -> Result<Sy
pub fn do_poll(poll_fds: &[PollFd], timeout: Option<&Duration>, ctx: &Context) -> Result<usize> { pub fn do_poll(poll_fds: &[PollFd], timeout: Option<&Duration>, ctx: &Context) -> Result<usize> {
let mut file_table = ctx.thread_local.file_table().borrow_mut(); let mut file_table = ctx.thread_local.file_table().borrow_mut();
let (result, files) = if let Some(file_table_inner) = file_table.get() {
hold_files(poll_fds, file_table_inner, Cow::Borrowed) let poll_files = if let Some(file_table_inner) = file_table.get() {
PollFiles::new_borrowed(poll_fds, file_table_inner)
} else { } else {
let file_table_locked = file_table.read(); let file_table_locked = file_table.read();
hold_files(poll_fds, &file_table_locked, |file| { PollFiles::new_owned(poll_fds, &file_table_locked)
Cow::Owned(file.clone())
})
}; };
match result {
FileResult::AllValid => (),
FileResult::SomeInvalid => {
return Ok(count_all_events(poll_fds, &files));
}
}
let poller = match register_poller(poll_fds, files.as_ref()) { let poller = match poll_files.register_poller() {
PollerResult::AllRegistered(poller) => poller, PollerResult::Registered(poller) => poller,
PollerResult::EventFoundAt(index) => { PollerResult::FoundEvents(num_events) => return Ok(num_events),
let next = index + 1;
let remaining_events = count_all_events(&poll_fds[next..], &files[next..]);
return Ok(1 + remaining_events);
}
}; };
loop { loop {
@ -96,7 +84,7 @@ pub fn do_poll(poll_fds: &[PollFd], timeout: Option<&Duration>, ctx: &Context) -
Err(e) => return Err(e), Err(e) => return Err(e),
}; };
let num_events = count_all_events(poll_fds, &files); let num_events = poll_files.count_events();
if num_events > 0 { if num_events > 0 {
return Ok(num_events); return Ok(num_events);
} }
@ -105,83 +93,88 @@ pub fn do_poll(poll_fds: &[PollFd], timeout: Option<&Duration>, ctx: &Context) -
} }
} }
enum FileResult { struct PollFiles<'a> {
AllValid, poll_fds: &'a [PollFd],
SomeInvalid, files: CowFiles<'a>,
} }
/// Holds all the files we're going to poll. enum CowFiles<'a> {
fn hold_files<'a, F, R>( Borrowed(&'a FileTable),
poll_fds: &[PollFd], Owned(Vec<Option<Arc<dyn FileLike>>>),
file_table: &'a FileTable,
f: F,
) -> (FileResult, Vec<Option<R>>)
where
F: Fn(&'a Arc<dyn FileLike>) -> R,
{
let mut files = Vec::with_capacity(poll_fds.len());
let mut result = FileResult::AllValid;
for poll_fd in poll_fds.iter() {
let Some(fd) = poll_fd.fd() else {
files.push(None);
continue;
};
let Ok(file) = file_table.get_file(fd) else {
poll_fd.revents.set(IoEvents::NVAL);
result = FileResult::SomeInvalid;
files.push(None);
continue;
};
files.push(Some(f(file)));
} }
(result, files) impl<'a> PollFiles<'a> {
/// Creates `PollFiles` by holding the file table reference.
fn new_borrowed(poll_fds: &'a [PollFd], file_table: &'a FileTable) -> Self {
Self {
poll_fds,
files: CowFiles::Borrowed(file_table),
}
}
/// Creates `PollFiles` by cloning all files that we're going to poll.
fn new_owned(poll_fds: &'a [PollFd], file_table: &FileTable) -> Self {
let files = poll_fds
.iter()
.map(|poll_fd| {
poll_fd
.fd()
.and_then(|fd| file_table.get_file(fd).ok().cloned())
})
.collect();
Self {
poll_fds,
files: CowFiles::Owned(files),
}
}
} }
enum PollerResult { enum PollerResult {
AllRegistered(Poller), Registered(Poller),
EventFoundAt(usize), FoundEvents(usize),
} }
impl PollFiles<'_> {
/// Registers the files with a poller, or exits early if some events are detected. /// Registers the files with a poller, or exits early if some events are detected.
fn register_poller(poll_fds: &[PollFd], files: &[Option<Cow<Arc<dyn FileLike>>>]) -> PollerResult { fn register_poller(&self) -> PollerResult {
let mut poller = Poller::new(); let mut poller = Poller::new();
for (i, (poll_fd, file)) in poll_fds.iter().zip(files.iter()).enumerate() { for (index, poll_fd) in self.poll_fds.iter().enumerate() {
let Some(file) = file else { let events = if let Some(file) = self.file_at(index) {
continue; file.poll(poll_fd.events(), Some(poller.as_handle_mut()))
} else {
IoEvents::NVAL
}; };
let events = file.poll(poll_fd.events(), Some(poller.as_handle_mut()));
if events.is_empty() { if events.is_empty() {
continue; continue;
} }
poll_fd.revents().set(events); poll_fd.revents().set(events);
return PollerResult::EventFoundAt(i); return PollerResult::FoundEvents(1 + self.count_events_from(1 + index));
} }
PollerResult::AllRegistered(poller) PollerResult::Registered(poller)
} }
/// Counts the number of the ready files. /// Counts the number of the ready files.
fn count_all_events(poll_fds: &[PollFd], files: &[Option<Cow<Arc<dyn FileLike>>>]) -> usize { fn count_events(&self) -> usize {
self.count_events_from(0)
}
/// Counts the number of the ready files from the given index.
fn count_events_from(&self, start: usize) -> usize {
let mut counter = 0; let mut counter = 0;
for (poll_fd, file) in poll_fds.iter().zip(files.iter()) { for index in start..self.poll_fds.len() {
let Some(file) = file else { let poll_fd = &self.poll_fds[index];
if !poll_fd.revents.get().is_empty() {
// This is only possible for POLLNVAL. let events = if let Some(file) = self.file_at(index) {
counter += 1; file.poll(poll_fd.events(), None)
} } else {
continue; IoEvents::NVAL
}; };
let events = file.poll(poll_fd.events(), None);
if events.is_empty() { if events.is_empty() {
continue; continue;
} }
@ -193,6 +186,17 @@ fn count_all_events(poll_fds: &[PollFd], files: &[Option<Cow<Arc<dyn FileLike>>>
counter counter
} }
fn file_at(&self, index: usize) -> Option<&dyn FileLike> {
match &self.files {
CowFiles::Borrowed(table) => self.poll_fds[index]
.fd()
.and_then(|fd| table.get_file(fd).ok())
.map(Arc::as_ref),
CowFiles::Owned(files) => files[index].as_deref(),
}
}
}
// https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/poll.h // https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/poll.h
#[derive(Debug, Clone, Copy, Pod)] #[derive(Debug, Clone, Copy, Pod)]
#[repr(C)] #[repr(C)]