| // Copyright 2022 The ChromiumOS Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| use std::io; | |
| use std::os::fd::OwnedFd; | |
| use std::{ | |
| mem, | |
| ops::Deref, | |
| os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}, | |
| ptr, | |
| time::Duration, | |
| }; | |
| use libc::{c_void, eventfd, read, write, POLLIN}; | |
| use crate::generate_scoped_event; | |
| use nix::Error; | |
| use nix::Result; | |
| /// Return a timespec filed with the specified Duration `duration`. | |
| #[allow(clippy::useless_conversion)] | |
| pub fn duration_to_timespec(duration: Duration) -> libc::timespec { | |
| // nsec always fits in i32 because subsec_nanos is defined to be less than one billion. | |
| let nsec = duration.subsec_nanos() as i32; | |
| libc::timespec { | |
| tv_sec: duration.as_secs() as libc::time_t, | |
| tv_nsec: nsec.into(), | |
| } | |
| } | |
| /// A safe wrapper around a Linux eventfd (man 2 eventfd). | |
| /// | |
| /// An eventfd is useful because it is sendable across processes and can be used for signaling in | |
| /// and out of the KVM API. They can also be polled like any other file descriptor. | |
| #[derive(Debug)] | |
| pub struct EventFd { | |
| event_handle: OwnedFd, | |
| } | |
| /// Wrapper around the return value of doing a read on an EventFd which distinguishes between | |
| /// getting a valid count of the number of times the eventfd has been written to and timing out | |
| /// waiting for the count to be non-zero. | |
| #[derive(Debug, PartialEq, Eq)] | |
| pub enum EventReadResult { | |
| Count(u64), | |
| Timeout, | |
| } | |
| impl EventFd { | |
| /// Creates a new blocking EventFd with an initial value of 0. | |
| pub fn new() -> Result<EventFd> { | |
| // This is safe because eventfd merely allocated an eventfd for our process and we handle | |
| // the error case. | |
| let ret = unsafe { eventfd(0, 0) }; | |
| if ret < 0 { | |
| return Err(Error::last()); | |
| } | |
| // This is safe because we checked ret for success and know the kernel gave us an fd that we | |
| // own. | |
| Ok(EventFd { | |
| event_handle: unsafe { OwnedFd::from_raw_fd(ret) }, | |
| }) | |
| } | |
| /// Adds `v` to the eventfd's count, blocking until this won't overflow the count. | |
| pub fn write(&self, v: u64) -> Result<()> { | |
| // This is safe because we made this fd and the pointer we pass can not overflow because we | |
| // give the syscall's size parameter properly. | |
| let ret = unsafe { | |
| write( | |
| self.as_raw_fd(), | |
| &v as *const u64 as *const c_void, | |
| mem::size_of::<u64>(), | |
| ) | |
| }; | |
| if ret <= 0 { | |
| return Err(Error::last()); | |
| } | |
| Ok(()) | |
| } | |
| /// Blocks until the the eventfd's count is non-zero, then resets the count to zero. | |
| pub fn read(&self) -> Result<u64> { | |
| let mut buf: u64 = 0; | |
| let ret = unsafe { | |
| // This is safe because we made this fd and the pointer we pass can not overflow because | |
| // we give the syscall's size parameter properly. | |
| read( | |
| self.as_raw_fd(), | |
| &mut buf as *mut u64 as *mut c_void, | |
| mem::size_of::<u64>(), | |
| ) | |
| }; | |
| if ret <= 0 { | |
| return Err(Error::last()); | |
| } | |
| Ok(buf) | |
| } | |
| /// Blocks for a maximum of `timeout` duration until the the eventfd's count is non-zero. If | |
| /// a timeout does not occur then the count is returned as a EventReadResult::Count(count), | |
| /// and the count is reset to 0. If a timeout does occur then this function will return | |
| /// EventReadResult::Timeout. | |
| pub fn read_timeout(&self, timeout: Duration) -> Result<EventReadResult> { | |
| let mut pfd = libc::pollfd { | |
| fd: self.as_raw_fd(), | |
| events: POLLIN, | |
| revents: 0, | |
| }; | |
| let timeoutspec: libc::timespec = duration_to_timespec(timeout); | |
| // Safe because this only modifies |pfd| and we check the return value | |
| let ret = unsafe { | |
| libc::ppoll( | |
| &mut pfd as *mut libc::pollfd, | |
| 1, | |
| &timeoutspec, | |
| ptr::null_mut(), | |
| ) | |
| }; | |
| if ret < 0 { | |
| return Err(Error::last()); | |
| } | |
| // no return events (revents) means we got a timeout | |
| if pfd.revents == 0 { | |
| return Ok(EventReadResult::Timeout); | |
| } | |
| let mut buf = 0u64; | |
| // This is safe because we made this fd and the pointer we pass can not overflow because | |
| // we give the syscall's size parameter properly. | |
| let ret = unsafe { | |
| libc::read( | |
| self.as_raw_fd(), | |
| &mut buf as *mut _ as *mut c_void, | |
| mem::size_of::<u64>(), | |
| ) | |
| }; | |
| if ret < 0 { | |
| return Err(Error::last()); | |
| } | |
| Ok(EventReadResult::Count(buf)) | |
| } | |
| /// Clones this EventFd, internally creating a new file descriptor. The new EventFd will share | |
| /// the same underlying count within the kernel. | |
| pub fn try_clone(&self) -> io::Result<EventFd> { | |
| self.event_handle | |
| .try_clone() | |
| .map(|event_handle| EventFd { event_handle }) | |
| } | |
| } | |
| impl AsRawFd for EventFd { | |
| fn as_raw_fd(&self) -> RawFd { | |
| self.event_handle.as_raw_fd() | |
| } | |
| } | |
| impl FromRawFd for EventFd { | |
| unsafe fn from_raw_fd(fd: RawFd) -> Self { | |
| EventFd { | |
| event_handle: OwnedFd::from_raw_fd(fd), | |
| } | |
| } | |
| } | |
| impl IntoRawFd for EventFd { | |
| fn into_raw_fd(self) -> RawFd { | |
| self.event_handle.into_raw_fd() | |
| } | |
| } | |
| generate_scoped_event!(EventFd); | |
| #[cfg(test)] | |
| mod tests { | |
| use super::*; | |
| #[test] | |
| fn new() { | |
| EventFd::new().unwrap(); | |
| } | |
| #[test] | |
| fn read_write() { | |
| let evt = EventFd::new().unwrap(); | |
| evt.write(55).unwrap(); | |
| assert_eq!(evt.read(), Ok(55)); | |
| } | |
| #[test] | |
| fn clone() { | |
| let evt = EventFd::new().unwrap(); | |
| let evt_clone = evt.try_clone().unwrap(); | |
| evt.write(923).unwrap(); | |
| assert_eq!(evt_clone.read(), Ok(923)); | |
| } | |
| #[test] | |
| fn scoped_event() { | |
| let scoped_evt = ScopedEvent::new().unwrap(); | |
| let evt_clone: EventFd = scoped_evt.try_clone().unwrap(); | |
| drop(scoped_evt); | |
| assert_eq!(evt_clone.read(), Ok(1)); | |
| } | |
| #[test] | |
| fn eventfd_from_scoped_event() { | |
| let scoped_evt = ScopedEvent::new().unwrap(); | |
| let evt: EventFd = scoped_evt.into(); | |
| evt.write(1).unwrap(); | |
| } | |
| #[test] | |
| fn timeout() { | |
| let evt = EventFd::new().expect("failed to create eventfd"); | |
| assert_eq!( | |
| evt.read_timeout(Duration::from_millis(1)) | |
| .expect("failed to read from eventfd with timeout"), | |
| EventReadResult::Timeout | |
| ); | |
| } | |
| } |