From a5adc0cc69327695fbb5a9898e167065039d679a Mon Sep 17 00:00:00 2001 From: Wildan M Date: Sat, 7 Mar 2026 19:04:32 +0700 Subject: [PATCH] Avoid heap allocation on event --- Cargo.lock | 7 +++++ Cargo.toml | 1 + src/event.rs | 72 +++++++++++++++++++++++++++-------------------- src/scheme/irq.rs | 3 +- 4 files changed, 51 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2dc10c0160..6b1ae0e164 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -122,6 +122,7 @@ dependencies = [ "rustc-demangle", "sbi-rt", "slab", + "smallvec", "spin", "toml", "x86", @@ -289,6 +290,12 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + [[package]] name = "spin" version = "0.9.8" diff --git a/Cargo.toml b/Cargo.toml index de35a68c8b..9c90185c32 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ redox-path = "0.2.0" redox_syscall = { version = "0.7.3", default-features = false } rmm = { path = "rmm", default-features = false } slab = { version = "0.4", default-features = false } +smallvec = { version = "1.15.1", default-features = false } spin = { version = "0.9.8" } # TODO: Remove indexmap = { version = "2.5.0", default-features = false } diff --git a/src/event.rs b/src/event.rs index 052c577a99..c23f8d2c66 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,6 +1,7 @@ -use alloc::{sync::Arc, vec::Vec}; +use alloc::sync::Arc; use core::sync::atomic::{AtomicUsize, Ordering}; -use hashbrown::{hash_map::DefaultHashBuilder, HashMap, HashSet}; +use hashbrown::{hash_map::DefaultHashBuilder, HashMap}; +use smallvec::SmallVec; use spin::Once; use syscall::data::GlobalSchemes; @@ -193,70 +194,79 @@ pub fn unregister_file(scheme: SchemeId, number: usize, token: &mut CleanLockTok // // } +const MAX_EVENT: usize = 8; + +#[must_use] fn trigger_inner( scheme: SchemeId, number: usize, flags: EventFlags, - todo: &mut Vec, + todo: &mut SmallVec<[EventQueueId; MAX_EVENT]>, + offset: &mut usize, token: &mut CleanLockToken, -) { - let mut matching_keys = Vec::new(); +) -> bool { + let mut matching_keys: SmallVec<[(QueueKey, EventFlags); MAX_EVENT]> = SmallVec::new(); + let mut full = false; + { let registry = registry(token); if let Some(queue_list) = registry.get(&RegKey { scheme, number }) { - for (queue_key, &queue_flags) in queue_list.iter() { + for (queue_key, &queue_flags) in queue_list.iter().skip(*offset) { let common_flags = flags & queue_flags; if !common_flags.is_empty() { + if matching_keys.len() == matching_keys.inline_size() { + full = true; + break; + } matching_keys.push((queue_key.clone(), common_flags)); } + *offset += 1; } } } - let mut queue_to_send: Vec<(Event, Arc)> = Vec::new(); + while let Some((queue_key, common_flags)) = matching_keys.pop() { + let Some(queue) = queues(token.token()).get(&queue_key.queue).cloned() else { + continue; + }; - for (queue_key, common_flags) in matching_keys { - let queue_opt = queues(token.token()).get(&queue_key.queue).cloned(); + let event = Event { + id: queue_key.id, + flags: common_flags, + data: queue_key.data, + }; - if let Some(queue) = queue_opt { - queue_to_send.push(( - Event { - id: queue_key.id, - flags: common_flags, - data: queue_key.data, - }, - queue, - )); - todo.push(queue_key.queue); - } - } - - for (event, queue) in queue_to_send { + todo.push(queue_key.queue); queue.queue.send(event, token); if let Some(queue) = Arc::into_inner(queue) { queue.into_drop(token); } } + + full } pub fn trigger(scheme: SchemeId, number: usize, flags: EventFlags, token: &mut CleanLockToken) { + let mut todo = SmallVec::<[EventQueueId; MAX_EVENT]>::new(); + let mut done = SmallVec::<[EventQueueId; MAX_EVENT]>::new(); + // First trigger with the original file - let mut todo = Vec::new(); - trigger_inner(scheme, number, flags, &mut todo, token); + let mut offset = 0; + while trigger_inner(scheme, number, flags, &mut todo, &mut offset, token) {} // Handle triggers on queues - //TODO: can this be done with limited allocations? - let mut done = HashSet::new(); while let Some(queue_id) = todo.pop() { - if !done.contains(&queue_id) { - trigger_inner( + if let Err(insert_idx) = done.binary_search(&queue_id) { + done.insert(insert_idx, queue_id); + let mut offset = 0; + while trigger_inner( GlobalSchemes::Event.scheme_id(), queue_id.into(), EventFlags::EVENT_READ, &mut todo, + &mut offset, token, - ); - done.insert(queue_id); + ) {} } } } diff --git a/src/scheme/irq.rs b/src/scheme/irq.rs index 6fd8bc5c73..c556840e4a 100644 --- a/src/scheme/irq.rs +++ b/src/scheme/irq.rs @@ -10,6 +10,7 @@ use core::{ use alloc::{string::String, vec::Vec}; use hashbrown::{hash_map::DefaultHashBuilder, HashMap}; +use smallvec::SmallVec; use spin::{Mutex, Once}; use syscall::{ data::GlobalSchemes, @@ -61,7 +62,7 @@ const INO_PHANDLE: u64 = 0x8003_0000_0000_0000; /// Add to the input queue pub fn irq_trigger(irq: u8, token: &mut CleanLockToken) { COUNTS.lock()[irq as usize] += 1; - let fds: Vec = { + let fds: SmallVec<[usize; 8]> = { HANDLES .read(token.token()) .iter()