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