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::{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
23pub 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 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}