Skip to main content

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
29pub struct EventFdFileObject {
30    inner: Mutex<u64>,
31    eventfd_type: EventFdType,
32    wait_queue: WaitQueue,
33}
34
35pub fn new_eventfd<L>(
36    locked: &mut Locked<L>,
37    current_task: &CurrentTask,
38    value: u32,
39    eventfd_type: EventFdType,
40    blocking: bool,
41) -> FileHandle
42where
43    L: LockEqualOrBefore<FileOpsCore>,
44{
45    let open_flags = if blocking { OpenFlags::RDWR } else { OpenFlags::RDWR | OpenFlags::NONBLOCK };
46    Anon::new_private_file(
47        locked,
48        current_task,
49        Box::new(EventFdFileObject {
50            inner: Mutex::new(value.into()),
51            eventfd_type,
52            wait_queue: WaitQueue::default(),
53        }),
54        open_flags,
55        "[eventfd]",
56    )
57}
58
59impl FileOps for EventFdFileObject {
60    fileops_impl_nonseekable!();
61    fileops_impl_noop_sync!();
62
63    fn write(
64        &self,
65        locked: &mut Locked<FileOpsCore>,
66        file: &FileObject,
67        current_task: &CurrentTask,
68        offset: usize,
69        data: &mut dyn InputBuffer,
70    ) -> Result<usize, Errno> {
71        debug_assert!(offset == 0);
72        file.blocking_op(locked, current_task, FdEvents::POLLOUT | FdEvents::POLLHUP, None, |_| {
73            let written_data = data.read_to_array::<DATA_SIZE>()?;
74            let add_value = u64::from_ne_bytes(written_data);
75            if add_value == u64::MAX {
76                return error!(EINVAL);
77            }
78
79            let should_notify = {
80                let mut inner = self.inner.lock();
81                let headroom = u64::MAX - *inner - 1;
82                if headroom < add_value {
83                    return error!(EAGAIN);
84                }
85                *inner += add_value;
86                *inner > 0
87            };
88            if should_notify {
89                self.wait_queue.notify_fd_events(FdEvents::POLLIN);
90            }
91            Ok(DATA_SIZE)
92        })
93    }
94
95    fn read(
96        &self,
97        locked: &mut Locked<FileOpsCore>,
98        file: &FileObject,
99        current_task: &CurrentTask,
100        offset: usize,
101        data: &mut dyn OutputBuffer,
102    ) -> Result<usize, Errno> {
103        debug_assert!(offset == 0);
104        file.blocking_op(locked, current_task, FdEvents::POLLIN | FdEvents::POLLHUP, None, |_| {
105            if data.available() < DATA_SIZE {
106                return error!(EINVAL);
107            }
108
109            let return_value = {
110                let mut inner = self.inner.lock();
111                if *inner == 0 {
112                    return error!(EAGAIN);
113                }
114
115                match self.eventfd_type {
116                    EventFdType::Counter => {
117                        let start_value = *inner;
118                        *inner = 0;
119                        start_value
120                    }
121                    EventFdType::Semaphore => {
122                        *inner -= 1;
123                        1
124                    }
125                }
126            };
127            data.write_all(&return_value.to_ne_bytes())?;
128            self.wait_queue.notify_fd_events(FdEvents::POLLOUT);
129
130            Ok(DATA_SIZE)
131        })
132    }
133
134    fn wait_async(
135        &self,
136        _locked: &mut Locked<FileOpsCore>,
137        _file: &FileObject,
138        _current_task: &CurrentTask,
139        waiter: &Waiter,
140        events: FdEvents,
141        handler: EventHandler,
142    ) -> Option<WaitCanceler> {
143        Some(self.wait_queue.wait_async_fd_events(waiter, events, handler))
144    }
145
146    fn query_events(
147        &self,
148        _locked: &mut Locked<FileOpsCore>,
149        _file: &FileObject,
150        _current_task: &CurrentTask,
151    ) -> Result<FdEvents, Errno> {
152        let inner = self.inner.lock();
153        // TODO check for error and HUP events
154        let mut events = FdEvents::empty();
155        if *inner > 0 {
156            events |= FdEvents::POLLIN;
157        }
158        if *inner < u64::MAX - 1 {
159            events |= FdEvents::POLLOUT;
160        }
161        Ok(events)
162    }
163}