From 48faad1bc60c818cd472ac43389e44e4446ff020 Mon Sep 17 00:00:00 2001 From: Zach Reizner Date: Wed, 3 May 2017 14:29:14 -0700 Subject: [PATCH] sys_util: add eventfd module In KVM, eventfd's are essential for sending and receiving signals to the VM. This CL adds a safe wrapper around their usage. TEST=cargo test BUG=None Change-Id: I04cd9036db156bfa8b9bd49281347a2460fbff2c Reviewed-on: https://chromium-review.googlesource.com/496988 Commit-Ready: Dylan Reid Tested-by: Zach Reizner Reviewed-by: Dylan Reid --- sys_util/src/eventfd.rs | 109 ++++++++++++++++++++++++++++++++++++++++ sys_util/src/lib.rs | 2 + 2 files changed, 111 insertions(+) create mode 100644 sys_util/src/eventfd.rs diff --git a/sys_util/src/eventfd.rs b/sys_util/src/eventfd.rs new file mode 100644 index 0000000000..0540b5d8b2 --- /dev/null +++ b/sys_util/src/eventfd.rs @@ -0,0 +1,109 @@ +// Copyright 2017 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::mem; +use std::fs::File; +use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; + +use libc::{read, write, eventfd, dup, c_void}; + +use {Result, errno_result}; + +/// 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. +pub struct EventFd { + eventfd: File, +} + +impl EventFd { + /// Creates a new blocking EventFd with an initial value of 0. + pub fn new() -> Result { + // 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 errno_result(); + } + // This is safe because we checked ret for success and know the kernel gave us an fd that we + // own. + Ok(EventFd { eventfd: unsafe { File::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::()) + }; + if ret <= 0 { + return errno_result(); + } + Ok(()) + } + + /// Blocks until the the eventfd's count is non-zero, then resets the count to zero. + pub fn read(&self) -> Result { + 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::()) + }; + if ret <= 0 { + return errno_result(); + } + Ok(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) -> Result { + // This is safe because we made this fd and properly check that it returns without error. + let ret = unsafe { dup(self.as_raw_fd()) }; + if ret < 0 { + return errno_result(); + } + // This is safe because we checked ret for success and know the kernel gave us an fd that we + // own. + Ok(EventFd { eventfd: unsafe { File::from_raw_fd(ret) } }) + } +} + +impl AsRawFd for EventFd { + fn as_raw_fd(&self) -> RawFd { + self.eventfd.as_raw_fd() + } +} + +#[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)); + } +} diff --git a/sys_util/src/lib.rs b/sys_util/src/lib.rs index 342504f326..4967e23d71 100644 --- a/sys_util/src/lib.rs +++ b/sys_util/src/lib.rs @@ -7,8 +7,10 @@ extern crate libc; mod mmap; +mod eventfd; mod errno; pub use mmap::*; +pub use eventfd::*; pub use errno::{Error, Result}; use errno::errno_result;