starnix_core/vfs/
eventfd.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::task::{CurrentTask, EventHandler, WaitCanceler, WaitQueue, Waiter};
6use crate::vfs::buffers::{InputBuffer, InputBufferExt as _, OutputBuffer};
7use crate::vfs::{
8    Anon, FileHandle, FileObject, FileOps, fileops_impl_nonseekable, fileops_impl_noop_sync,
9};
10use starnix_sync::{FileOpsCore, LockEqualOrBefore, Locked, Mutex};
11use starnix_uapi::error;
12use starnix_uapi::errors::Errno;
13use starnix_uapi::open_flags::OpenFlags;
14use starnix_uapi::vfs::FdEvents;
15
16const DATA_SIZE: usize = 8;
17
18pub enum EventFdType {
19    Counter,
20    Semaphore,
21}
22
23/// The eventfd file object has two modes of operation:
24/// 1) Counter: Write adds to the value and read returns the value while setting it to zero.
25/// 2) Semaphore: Write adds one to the counter and read decrements it and returns 1.
26/// In both cases, if the value is 0, the read blocks or returns EAGAIN.
27/// See https://man7.org/linux/man-pages/man2/eventfd.2.html
28
29struct EventFdInner {
30    value: u64,
31    wait_queue: WaitQueue,
32}
33
34pub struct EventFdFileObject {
35    inner: Mutex<EventFdInner>,
36    eventfd_type: EventFdType,
37}
38
39pub fn new_eventfd<L>(
40    locked: &mut Locked<L>,
41    current_task: &CurrentTask,
42    value: u32,
43    eventfd_type: EventFdType,
44    blocking: bool,
45) -> FileHandle
46where
47    L: LockEqualOrBefore<FileOpsCore>,
48{
49    let open_flags = if blocking { OpenFlags::RDWR } else { OpenFlags::RDWR | OpenFlags::NONBLOCK };
50    Anon::new_private_file(
51        locked,
52        current_task,
53        Box::new(EventFdFileObject {
54            inner: Mutex::new(EventFdInner {
55                value: value.into(),
56                wait_queue: WaitQueue::default(),
57            }),
58            eventfd_type,
59        }),
60        open_flags,
61        "[eventfd]",
62    )
63}
64
65impl FileOps for EventFdFileObject {
66    fileops_impl_nonseekable!();
67    fileops_impl_noop_sync!();
68
69    fn write(
70        &self,
71        locked: &mut Locked<FileOpsCore>,
72        file: &FileObject,
73        current_task: &CurrentTask,
74        offset: usize,
75        data: &mut dyn InputBuffer,
76    ) -> Result<usize, Errno> {
77        debug_assert!(offset == 0);
78        file.blocking_op(locked, current_task, FdEvents::POLLOUT | FdEvents::POLLHUP, None, |_| {
79            let written_data = data.read_to_array::<DATA_SIZE>()?;
80            let add_value = u64::from_ne_bytes(written_data);
81            if add_value == u64::MAX {
82                return error!(EINVAL);
83            }
84
85            // The maximum value of the counter is u64::MAX - 1
86            let mut inner = self.inner.lock();
87            let headroom = u64::MAX - inner.value - 1;
88            if headroom < add_value {
89                return error!(EAGAIN);
90            }
91            inner.value += add_value;
92            if inner.value > 0 {
93                inner.wait_queue.notify_fd_events(FdEvents::POLLIN);
94            }
95            Ok(DATA_SIZE)
96        })
97    }
98
99    fn read(
100        &self,
101        locked: &mut Locked<FileOpsCore>,
102        file: &FileObject,
103        current_task: &CurrentTask,
104        offset: usize,
105        data: &mut dyn OutputBuffer,
106    ) -> Result<usize, Errno> {
107        debug_assert!(offset == 0);
108        file.blocking_op(locked, current_task, FdEvents::POLLIN | FdEvents::POLLHUP, None, |_| {
109            if data.available() < DATA_SIZE {
110                return error!(EINVAL);
111            }
112
113            let mut inner = self.inner.lock();
114            if inner.value == 0 {
115                return error!(EAGAIN);
116            }
117
118            let return_value = match self.eventfd_type {
119                EventFdType::Counter => {
120                    let start_value = inner.value;
121                    inner.value = 0;
122                    start_value
123                }
124                EventFdType::Semaphore => {
125                    inner.value -= 1;
126                    1
127                }
128            };
129            data.write_all(&return_value.to_ne_bytes())?;
130            inner.wait_queue.notify_fd_events(FdEvents::POLLOUT);
131
132            Ok(DATA_SIZE)
133        })
134    }
135
136    fn wait_async(
137        &self,
138        _locked: &mut Locked<FileOpsCore>,
139        _file: &FileObject,
140        _current_task: &CurrentTask,
141        waiter: &Waiter,
142        events: FdEvents,
143        handler: EventHandler,
144    ) -> Option<WaitCanceler> {
145        Some(self.inner.lock().wait_queue.wait_async_fd_events(waiter, events, handler))
146    }
147
148    fn query_events(
149        &self,
150        _locked: &mut Locked<FileOpsCore>,
151        _file: &FileObject,
152        _current_task: &CurrentTask,
153    ) -> Result<FdEvents, Errno> {
154        let inner = self.inner.lock();
155        // TODO check for error and HUP events
156        let mut events = FdEvents::empty();
157        if inner.value > 0 {
158            events |= FdEvents::POLLIN;
159        }
160        if inner.value < u64::MAX - 1 {
161            events |= FdEvents::POLLOUT;
162        }
163        Ok(events)
164    }
165}