1use crate::fs::fuchsia::RemoteCounter;
6use crate::mm::MemoryAccessorExt;
7use crate::task::{
8 CurrentTask, EventHandler, ManyZxHandleSignalHandler, SignalHandler, SignalHandlerInner,
9 WaitCanceler, Waiter,
10};
11use crate::vfs::buffers::{InputBuffer, OutputBuffer};
12use crate::vfs::{
13 Anon, FdFlags, FdNumber, FileHandle, FileObject, FileOps, fileops_impl_nonseekable,
14 fileops_impl_noop_sync,
15};
16
17use starnix_lifecycle::AtomicUsizeCounter;
18use starnix_logging::{CATEGORY_STARNIX, impossible_error, log_warn, trace_duration};
19use starnix_sync::{FileOpsCore, LockEqualOrBefore, Locked, Unlocked};
20use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
21use starnix_uapi::errors::Errno;
22use starnix_uapi::open_flags::OpenFlags;
23use starnix_uapi::user_address::{UserAddress, UserRef};
24use starnix_uapi::vfs::FdEvents;
25use starnix_uapi::{
26 SYNC_IOC_MAGIC, c_char, errno, error, sync_fence_info, sync_file_info, sync_merge_data,
27};
28use std::collections::HashSet;
29use std::sync::Arc;
30use zx::HandleBased;
31
32const SYNC_IOC_MERGE: u8 = 3;
46const SYNC_IOC_FILE_INFO: u8 = 4;
47
48#[derive(Clone, Debug)]
49pub enum Timeline {
50 Magma,
51 Hwc,
52}
53
54#[derive(PartialEq, Copy, Clone)]
55pub enum Status {
57 Active = 0,
58 Signaled = 1,
59}
60
61#[derive(Clone)]
62pub struct SyncPoint {
63 pub timeline: Timeline,
64 pub counter: Arc<zx::Counter>,
65}
66
67impl SyncPoint {
68 pub fn new(timeline: Timeline, counter: zx::Counter) -> SyncPoint {
69 SyncPoint { timeline, counter: Arc::new(counter) }
70 }
71}
72
73pub struct SyncFence {
74 pub sync_points: Vec<SyncPoint>,
75}
76
77pub struct SyncFile {
78 pub name: [u8; 32],
79 pub fence: SyncFence,
80}
81
82struct FenceState {
83 status: Status,
84 timestamp_ns: u64,
85}
86
87impl SyncFile {
88 const SIGNALS: zx::Signals = zx::Signals::COUNTER_SIGNALED;
89
90 pub fn new_file<L>(
92 locked: &mut Locked<L>,
93 current_task: &CurrentTask,
94 name: [u8; 32],
95 fence: SyncFence,
96 ) -> Result<FileHandle, Errno>
97 where
98 L: LockEqualOrBefore<FileOpsCore>,
99 {
100 Ok(Anon::new_private_file(
103 locked,
104 current_task,
105 Box::new(SyncFile::new(name, fence)),
106 OpenFlags::RDWR,
107 "sync_file",
108 ))
109 }
110
111 pub fn new(name: [u8; 32], fence: SyncFence) -> SyncFile {
112 SyncFile { name, fence }
113 }
114
115 fn get_fence_state(&self) -> Vec<FenceState> {
116 let mut state: Vec<FenceState> = vec![];
117
118 for sync_point in &self.fence.sync_points {
119 if sync_point.counter.wait_one(Self::SIGNALS, zx::MonotonicInstant::ZERO).to_result()
120 == Err(zx::Status::TIMED_OUT)
121 {
122 state.push(FenceState { status: Status::Active, timestamp_ns: 0 });
123 } else {
124 state.push(FenceState {
125 status: Status::Signaled,
126 timestamp_ns: sync_point.counter.read().unwrap() as u64,
127 });
128 }
129 }
130 state
131 }
132}
133
134impl FileOps for SyncFile {
135 fileops_impl_nonseekable!();
136 fileops_impl_noop_sync!();
137
138 fn to_handle(
139 &self,
140 _file: &FileObject,
141 _current_task: &CurrentTask,
142 ) -> Result<Option<zx::NullableHandle>, Errno> {
143 error!(ENOTSUP)
144 }
145
146 fn get_handles(
147 &self,
148 _file: &FileObject,
149 _current_task: &CurrentTask,
150 ) -> Result<Vec<zx::NullableHandle>, Errno> {
151 let mut handles = Vec::with_capacity(self.fence.sync_points.len());
152 for sync_point in &self.fence.sync_points {
153 let handle = sync_point
154 .counter
155 .duplicate_handle(zx::Rights::SAME_RIGHTS)
156 .map_err(impossible_error)?
157 .into();
158 handles.push(handle);
159 }
160 Ok(handles)
161 }
162
163 fn ioctl(
164 &self,
165 locked: &mut Locked<Unlocked>,
166 _file: &FileObject,
167 current_task: &CurrentTask,
168 request: u32,
169 arg: SyscallArg,
170 ) -> Result<SyscallResult, Errno> {
171 let user_addr = UserAddress::from(arg);
172 let ioctl_type = (request >> 8) as u8;
173 let ioctl_number = request as u8;
174
175 if ioctl_type != SYNC_IOC_MAGIC {
176 log_warn!("Unexpected type {:?}", ioctl_type);
177 return error!(EINVAL);
178 }
179
180 match ioctl_number {
181 SYNC_IOC_MERGE => {
182 trace_duration!(CATEGORY_STARNIX, "SyncFileMerge");
183 let user_ref = UserRef::new(user_addr);
184 let mut merge_data: sync_merge_data = current_task.read_object(user_ref)?;
185 let file2 = current_task.files.get(FdNumber::from_raw(merge_data.fd2))?;
186
187 let mut fence = SyncFence { sync_points: vec![] };
188 let mut set = HashSet::<zx::Koid>::new();
189
190 for sync_point in &self.fence.sync_points {
191 let koid = sync_point.counter.koid().unwrap();
192 if set.insert(koid) {
193 fence.sync_points.push(sync_point.clone());
194 }
195 }
196
197 if let Some(file2) = file2.downcast_file::<SyncFile>() {
198 for sync_point in &file2.fence.sync_points {
199 let koid = sync_point.counter.koid().unwrap();
200 if set.insert(koid) {
201 fence.sync_points.push(sync_point.clone());
202 }
203 }
204 } else if let Some(file2) = file2.downcast_file::<RemoteCounter>() {
205 let counter = file2.duplicate_handle()?;
206 let koid = counter.koid().map_err(impossible_error)?;
207 if set.insert(koid) {
208 fence.sync_points.push(SyncPoint::new(Timeline::Hwc, counter.into()));
209 }
210 } else {
211 return error!(EINVAL);
212 }
213
214 let mut i = 0 as usize;
216 let mut last_signaled_timestamp_ns = 0;
217 let mut last_signaled_sync_point: Option<SyncPoint> = None;
218 while i < fence.sync_points.len() {
219 if fence.sync_points[i]
220 .counter
221 .wait_one(Self::SIGNALS, zx::MonotonicInstant::ZERO)
222 .to_result()
223 != Err(zx::Status::TIMED_OUT)
224 {
225 let timestamp_ns =
226 fence.sync_points[i].counter.read().map_err(|_| errno!(EIO))?;
227 let removed = fence.sync_points.remove(i);
228 if i == 0 && timestamp_ns >= last_signaled_timestamp_ns {
229 last_signaled_timestamp_ns = timestamp_ns;
230 last_signaled_sync_point = Some(removed);
231 }
232 continue;
233 }
234 i += 1;
235 }
236 if fence.sync_points.is_empty() {
237 fence.sync_points.push(last_signaled_sync_point.expect("No sync points left."));
238 }
239
240 let name = merge_data.name.map(|x| x as u8);
241 let file = SyncFile::new_file(locked, current_task, name, fence)?;
242
243 let fd = current_task.add_file(locked, file, FdFlags::empty())?;
244 merge_data.fence = fd.raw();
245
246 current_task.write_object(user_ref, &merge_data)?;
247 Ok(SUCCESS)
248 }
249 SYNC_IOC_FILE_INFO => {
250 trace_duration!(CATEGORY_STARNIX, "SyncFileInfo");
251 let user_ref = UserRef::new(user_addr);
252 let mut info: sync_file_info = current_task.read_object(user_ref)?;
253
254 for i in 0..self.name.len() {
255 info.name[i] = self.name[i] as c_char;
256 }
257 info.status = 0;
258
259 if info.num_fences == 0 {
260 info.num_fences = self.fence.sync_points.len() as u32;
261 } else if info.num_fences > self.fence.sync_points.len() as u32 {
262 return error!(EINVAL);
263 } else {
264 let fence_state = self.get_fence_state();
265 let mut user_addr = info.sync_fence_info;
266
267 let mut sync_file_status = 1;
268 for (i, state) in fence_state.iter().enumerate() {
269 if state.status == Status::Active {
270 sync_file_status = 0;
271 }
272 if i < info.num_fences as usize {
273 let mut fence_info = sync_fence_info {
275 status: state.status as i32,
276 timestamp_ns: state.timestamp_ns,
277 ..sync_fence_info::default()
278 };
279 let driver_name = match self.fence.sync_points[i].timeline {
280 Timeline::Magma => b"Magma\0",
281 Timeline::Hwc => b"Hwc\0\0\0",
282 };
283 assert!(driver_name.len() <= fence_info.driver_name.len());
284 for i in 0..driver_name.len() {
285 fence_info.driver_name[i] = driver_name[i] as c_char;
286 }
287
288 let fence_user_ref = UserRef::new(UserAddress::from(user_addr));
289 user_addr += std::mem::size_of::<sync_fence_info>() as u64;
290
291 current_task.write_object(fence_user_ref, &fence_info)?;
292 }
293 }
294
295 info.status = sync_file_status;
296 }
297
298 current_task.write_object(user_ref, &info)?;
299 Ok(SUCCESS)
300 }
301 _ => {
302 error!(EINVAL)
303 }
304 }
305 }
306
307 fn wait_async(
308 &self,
309 _locked: &mut Locked<FileOpsCore>,
310 _file: &FileObject,
311 _current_task: &CurrentTask,
312 waiter: &Waiter,
313 events: FdEvents,
314 event_handler: EventHandler,
315 ) -> Option<WaitCanceler> {
316 if !events.contains(FdEvents::POLLIN) {
317 return None;
318 }
319
320 let count = Arc::<AtomicUsizeCounter>::new(0.into());
321
322 let mut canceler = WaitCanceler::new_noop();
323
324 for sync_point in &self.fence.sync_points {
325 let signal_handler = SignalHandler {
326 inner: SignalHandlerInner::ManyZxHandle(ManyZxHandleSignalHandler {
327 count: self.fence.sync_points.len(),
328 counter: count.clone(),
329 expected_signals: Self::SIGNALS,
330 events: FdEvents::POLLIN,
331 }),
332 event_handler: event_handler.clone(),
333 err_code: None,
334 };
335
336 let canceler_result = waiter.wake_on_zircon_signals(
337 sync_point.counter.as_ref(),
338 Self::SIGNALS,
339 signal_handler,
340 );
341 let canceler_result = match canceler_result {
342 Ok(o) => o,
343 Err(e) => {
344 log_warn!("Error returned from wake_on_zircon_signals: {:?}", e);
345 return None;
346 }
347 };
348
349 if sync_point.counter.wait_one(Self::SIGNALS, zx::MonotonicInstant::ZERO).to_result()
355 == Err(zx::Status::TIMED_OUT)
356 {
357 canceler = WaitCanceler::merge_unbounded(
358 canceler,
359 WaitCanceler::new_port(canceler_result),
360 );
361 } else {
362 canceler_result.cancel();
363 count.next();
364 }
365 }
366
367 Some(canceler)
368 }
369
370 fn query_events(
371 &self,
372 _locked: &mut Locked<FileOpsCore>,
373 _file: &FileObject,
374 _current_task: &CurrentTask,
375 ) -> Result<FdEvents, Errno> {
376 let fence_state = self.get_fence_state();
377
378 for state in fence_state.iter() {
379 if state.status == Status::Active {
380 return Ok(FdEvents::empty());
381 }
382 }
383
384 Ok(FdEvents::POLLIN)
385 }
386
387 fn read(
388 &self,
389 _locked: &mut Locked<FileOpsCore>,
390 _file: &FileObject,
391 _current_task: &CurrentTask,
392 _offset: usize,
393 _data: &mut dyn OutputBuffer,
394 ) -> Result<usize, Errno> {
395 error!(ENODEV)
396 }
397
398 fn write(
399 &self,
400 _locked: &mut Locked<FileOpsCore>,
401 _file: &FileObject,
402 _current_task: &CurrentTask,
403 _offset: usize,
404 _data: &mut dyn InputBuffer,
405 ) -> Result<usize, Errno> {
406 error!(ENODEV)
407 }
408}