starnix_core/vfs/
eventfd.rs1use 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
23struct 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 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 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}