From 9e55cd84abbfe780ed71540fc0916cbefd09fcf3 Mon Sep 17 00:00:00 2001 From: Johannes Leupolz Date: Wed, 15 Apr 2026 22:20:39 +0000 Subject: [PATCH] Fix for poll and read. --- .../src/cuse_device/evdev_write_watcher.rs | 10 ++-- vuinputd/src/cuse_device/state.rs | 52 +++++++++++++++---- vuinputd/src/cuse_device/vuinput_poll.rs | 32 ++++++++---- vuinputd/src/cuse_device/vuinput_read.rs | 8 +-- 4 files changed, 73 insertions(+), 29 deletions(-) diff --git a/vuinputd/src/cuse_device/evdev_write_watcher.rs b/vuinputd/src/cuse_device/evdev_write_watcher.rs index 485ec03..ed4517b 100644 --- a/vuinputd/src/cuse_device/evdev_write_watcher.rs +++ b/vuinputd/src/cuse_device/evdev_write_watcher.rs @@ -106,15 +106,11 @@ fn evdev_write_watch_loop(shutdown: Arc, epoll: Arc) { let state = super::state::get_vuinput_state(&fh); if let Ok(state) = state { let mut state = state.lock().unwrap(); - - for handle in state.poll.take_waiters() { - unsafe { - fuse_lowlevel::fuse_lowlevel_notify_poll(handle.as_ptr()); - fuse_lowlevel::fuse_pollhandle_destroy(handle.as_ptr()); - } + let handle = state.poll.take_waiters(); + if let Some(mut handle)= handle { + handle.notify(); } state.poll.pollphase = PollPhase::Readable; - state.poll.pending.clear(); } } } diff --git a/vuinputd/src/cuse_device/state.rs b/vuinputd/src/cuse_device/state.rs index 7e9ce53..3fd4870 100644 --- a/vuinputd/src/cuse_device/state.rs +++ b/vuinputd/src/cuse_device/state.rs @@ -11,7 +11,6 @@ use ::cuse_lowlevel::*; use smallvec::SmallVec; use crate::process_tools::RequestingProcess; -use smallvec::smallvec; pub type PendingPollHandles = SmallVec<[*mut fuse_lowlevel::fuse_pollhandle; 1]>; @@ -61,6 +60,43 @@ impl Default for PollPhase { } } +#[derive(Debug)] +pub struct PollHandle { + ptr: NonNull, + has_been_completed: bool, +} + +impl PollHandle { + pub fn new(ptr: NonNull) -> Self { + Self { + ptr: ptr, + has_been_completed: false, + } + } + pub fn notify(&mut self) { + if !self.has_been_completed { + unsafe { + fuse_lowlevel::fuse_lowlevel_notify_poll(self.ptr.as_ptr()); + fuse_lowlevel::fuse_pollhandle_destroy(self.ptr.as_ptr()); + } + self.has_been_completed = true; + } + } +} + +impl Drop for PollHandle { + fn drop(&mut self) { + if !self.has_been_completed { + unsafe { + fuse_lowlevel::fuse_pollhandle_destroy(self.ptr.as_ptr()); + } + self.has_been_completed = true; + } + } +} + +unsafe impl Send for PollHandle {} + /// this data structure ensures poll and read are synchronized. /// poll() and read() must synchronize through one shared readines /// state, and the state transitions must be done under the same per-handle mutex. @@ -78,31 +114,29 @@ pub struct PollState { /// Pending FUSE poll waiters for this device. /// Optimized for the common case of 0 or 1 waiter, but supports /// multiple concurrent poll() callers correctly. - pub pending: SmallVec<[NonNull; 1]>, + pending: Option, } impl PollState { pub fn new() -> PollState { PollState { pollphase: PollPhase::Empty, - pending: smallvec![], + pending: None, } } pub fn has_waiters(&self) -> bool { - !self.pending.is_empty() + !self.pending.is_some() } - pub fn add_waiter(&mut self, handle: NonNull) { - self.pending.push(handle); + pub fn set_waiter(&mut self, handle: NonNull) { + self.pending = Some(PollHandle::new(handle)); } - pub fn take_waiters(&mut self) -> SmallVec<[NonNull; 1]> { + pub fn take_waiters(&mut self) -> Option { std::mem::take(&mut self.pending) } } -unsafe impl Send for PollState {} - #[derive(Debug)] pub struct VuInputState { pub file: File, diff --git a/vuinputd/src/cuse_device/vuinput_poll.rs b/vuinputd/src/cuse_device/vuinput_poll.rs index d01b3e7..80c6ccf 100644 --- a/vuinputd/src/cuse_device/vuinput_poll.rs +++ b/vuinputd/src/cuse_device/vuinput_poll.rs @@ -5,7 +5,7 @@ use crate::cuse_device::*; use crate::global_config::get_device_policy; use ::cuse_lowlevel::*; -use libc::{__s32, __u16, input_event}; +use libc::{__s32, __u16, input_event, POLLIN}; use libc::{off_t, size_t, EIO}; use libc::{uinput_abs_setup, uinput_setup}; use log::{debug, trace}; @@ -16,13 +16,18 @@ use std::ptr::NonNull; use uinput_ioctls::*; // https://github.com/libfuse/libfuse/blob/master/example/poll.c +// https://github.com/torvalds/linux/blob/f82b61de0f5dc58930fdb773b9e843573fcc374b/fs/fuse/file.c + +// Note that poll in fuse blocks (because it calls fuse_simple_request, which is designed to block) +// until the handle is notified. + pub unsafe extern "C" fn vuinput_poll( req: fuse_lowlevel::fuse_req_t, fi: *mut fuse_lowlevel::fuse_file_info, ph: *mut fuse_lowlevel::fuse_pollhandle, ) { - fuse_lowlevel::fuse_reply_err(req, EIO); - return; + //fuse_lowlevel::fuse_reply_err(req, EIO); + //return; let vuinput_state_mutex = get_vuinput_state(&VuFileHandle::from_fuse_file_info(fi.as_ref().unwrap())).unwrap(); @@ -32,16 +37,25 @@ pub unsafe extern "C" fn vuinput_poll( match vuinput_state.poll.pollphase { PollPhase::Empty => { - let ph = NonNull::::new(ph); - vuinput_state.poll.add_waiter(ph.unwrap()); + if ph != std::ptr::null_mut() { + let ph = NonNull::::new(ph); + vuinput_state.poll.set_waiter(ph.unwrap()); + } + fuse_lowlevel::fuse_reply_poll(req, 0); } PollPhase::Readable => { - fuse_lowlevel::fuse_lowlevel_notify_poll(ph); - fuse_lowlevel::fuse_pollhandle_destroy(ph); + if ph != std::ptr::null_mut() { + fuse_lowlevel::fuse_lowlevel_notify_poll(ph); + fuse_lowlevel::fuse_pollhandle_destroy(ph); + } + fuse_lowlevel::fuse_reply_poll(req, POLLIN.try_into().unwrap()); } PollPhase::Reading => { - fuse_lowlevel::fuse_lowlevel_notify_poll(ph); - fuse_lowlevel::fuse_pollhandle_destroy(ph); + if ph != std::ptr::null_mut() { + fuse_lowlevel::fuse_lowlevel_notify_poll(ph); + fuse_lowlevel::fuse_pollhandle_destroy(ph); + } + fuse_lowlevel::fuse_reply_poll(req, POLLIN.try_into().unwrap()); } } } diff --git a/vuinputd/src/cuse_device/vuinput_read.rs b/vuinputd/src/cuse_device/vuinput_read.rs index 8d5d451..9a6b411 100644 --- a/vuinputd/src/cuse_device/vuinput_read.rs +++ b/vuinputd/src/cuse_device/vuinput_read.rs @@ -23,8 +23,8 @@ pub unsafe extern "C" fn vuinput_read( "vuinput_read: offset needs to be 0 but is {}", _off ); - fuse_lowlevel::fuse_reply_err(_req, EIO); - return; + //fuse_lowlevel::fuse_reply_err(_req, EIO); + //return; let fh = &(*_fi).fh; let vuinput_state_mutex = @@ -41,10 +41,10 @@ pub unsafe extern "C" fn vuinput_read( vuinput_state.poll.pollphase = PollPhase::Reading; // read up to 24 bytes - println!("vuinput_read: read"); + //println!("vuinput_read: read"); let result = vuinput_state.file.read(&mut buffer); - println!("vuinput_read: read finished"); + //println!("vuinput_read: read finished"); match result { Ok(NORMAL_SIZE) => { if !is_compat {