blob: e8003f7694c7a9acdfa25306dcbb7ffa992bc9b5 [file] [log] [blame] [edit]
// Copyright 2021 The Fuchsia 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 starnix_lock::Mutex;
use crate::{
fs::{
buffers::{InputBuffer, OutputBuffer},
fileops_impl_nonseekable, Anon, FdEvents, FileHandle, FileObject, FileOps,
},
task::{CurrentTask, EventHandler, WaitCanceler, WaitQueue, Waiter},
types::{error, Errno, OpenFlags},
};
const DATA_SIZE: usize = 8;
pub enum EventFdType {
Counter,
Semaphore,
}
/// The eventfd file object has two modes of operation:
/// 1) Counter: Write adds to the value and read returns the value while setting it to zero.
/// 2) Semaphore: Write adds one to the counter and read decrements it and returns 1.
/// In both cases, if the value is 0, the read blocks or returns EAGAIN.
/// See https://man7.org/linux/man-pages/man2/eventfd.2.html
struct EventFdInner {
value: u64,
wait_queue: WaitQueue,
}
pub struct EventFdFileObject {
inner: Mutex<EventFdInner>,
eventfd_type: EventFdType,
}
pub fn new_eventfd(
current_task: &CurrentTask,
value: u32,
eventfd_type: EventFdType,
blocking: bool,
) -> FileHandle {
let open_flags = if blocking { OpenFlags::RDWR } else { OpenFlags::RDWR | OpenFlags::NONBLOCK };
Anon::new_file(
current_task,
Box::new(EventFdFileObject {
inner: Mutex::new(EventFdInner {
value: value.into(),
wait_queue: WaitQueue::default(),
}),
eventfd_type,
}),
open_flags,
)
}
impl FileOps for EventFdFileObject {
fileops_impl_nonseekable!();
fn write(
&self,
file: &FileObject,
current_task: &CurrentTask,
offset: usize,
data: &mut dyn InputBuffer,
) -> Result<usize, Errno> {
debug_assert!(offset == 0);
file.blocking_op(current_task, FdEvents::POLLOUT | FdEvents::POLLHUP, None, || {
let mut written_data = [0; DATA_SIZE];
data.read_exact(&mut written_data)?;
let add_value = u64::from_ne_bytes(written_data);
if add_value == u64::MAX {
return error!(EINVAL);
}
// The maximum value of the counter is u64::MAX - 1
let mut inner = self.inner.lock();
let headroom = u64::MAX - inner.value - 1;
if headroom < add_value {
return error!(EAGAIN);
}
inner.value += add_value;
if inner.value > 0 {
inner.wait_queue.notify_fd_events(FdEvents::POLLIN);
}
Ok(DATA_SIZE)
})
}
fn read(
&self,
file: &FileObject,
current_task: &CurrentTask,
offset: usize,
data: &mut dyn OutputBuffer,
) -> Result<usize, Errno> {
debug_assert!(offset == 0);
file.blocking_op(current_task, FdEvents::POLLIN | FdEvents::POLLHUP, None, || {
if data.available() < DATA_SIZE {
return error!(EINVAL);
}
let mut inner = self.inner.lock();
if inner.value == 0 {
return error!(EAGAIN);
}
let return_value = match self.eventfd_type {
EventFdType::Counter => {
let start_value = inner.value;
inner.value = 0;
start_value
}
EventFdType::Semaphore => {
inner.value -= 1;
1
}
};
data.write_all(&return_value.to_ne_bytes())?;
inner.wait_queue.notify_fd_events(FdEvents::POLLOUT);
Ok(DATA_SIZE)
})
}
fn wait_async(
&self,
_file: &FileObject,
_current_task: &CurrentTask,
waiter: &Waiter,
events: FdEvents,
handler: EventHandler,
) -> Option<WaitCanceler> {
Some(self.inner.lock().wait_queue.wait_async_fd_events(waiter, events, handler))
}
fn query_events(
&self,
_file: &FileObject,
_current_task: &CurrentTask,
) -> Result<FdEvents, Errno> {
let inner = self.inner.lock();
// TODO check for error and HUP events
let mut events = FdEvents::empty();
if inner.value > 0 {
events |= FdEvents::POLLIN;
}
if inner.value < u64::MAX - 1 {
events |= FdEvents::POLLOUT;
}
Ok(events)
}
}