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::AtomicCounter;
18use starnix_logging::{impossible_error, log_warn};
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, error, sync_fence_info, sync_file_info, sync_merge_data,
27};
28use std::collections::HashSet;
29use std::sync::Arc;
30
31const SYNC_IOC_MERGE: u8 = 3;
45const SYNC_IOC_FILE_INFO: u8 = 4;
46const TRACE_CATEGORY: &'static str = "gfx";
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 koid: std::sync::OnceLock<zx::Koid>,
66}
67
68impl SyncPoint {
69 pub fn new(timeline: Timeline, counter: zx::Counter) -> SyncPoint {
70 SyncPoint { timeline, counter: Arc::new(counter), koid: std::sync::OnceLock::new() }
71 }
72
73 pub fn with_koid(timeline: Timeline, counter: zx::Counter, koid: zx::Koid) -> SyncPoint {
74 let once_lock = std::sync::OnceLock::new();
75 let _ = once_lock.set(koid);
76 SyncPoint { timeline, counter: Arc::new(counter), koid: once_lock }
77 }
78
79 pub fn koid(&self) -> zx::Koid {
80 *self.koid.get_or_init(|| self.counter.koid().unwrap())
81 }
82}
83
84pub struct SyncFence {
85 pub sync_points: Vec<SyncPoint>,
86}
87
88pub struct SyncFile {
89 pub name: [u8; 32],
90 pub fence: SyncFence,
91}
92
93struct FenceState {
94 status: Status,
95 timestamp_ns: u64,
96}
97
98impl SyncFile {
99 const SIGNALS: zx::Signals = zx::Signals::COUNTER_SIGNALED;
100
101 pub fn new_file<L>(
103 locked: &mut Locked<L>,
104 current_task: &CurrentTask,
105 name: [u8; 32],
106 fence: SyncFence,
107 ) -> Result<FileHandle, Errno>
108 where
109 L: LockEqualOrBefore<FileOpsCore>,
110 {
111 Ok(Anon::new_private_file(
114 locked,
115 current_task,
116 Box::new(SyncFile::new(name, fence)),
117 OpenFlags::RDWR,
118 "sync_file",
119 ))
120 }
121
122 pub fn new(name: [u8; 32], fence: SyncFence) -> SyncFile {
123 SyncFile { name, fence }
124 }
125
126 fn get_fence_state(&self) -> Vec<FenceState> {
127 let mut state = Vec::with_capacity(self.fence.sync_points.len());
128
129 for sync_point in &self.fence.sync_points {
130 let timestamp_ns = sync_point.counter.read().unwrap() as u64;
131 if timestamp_ns > 0 {
132 state.push(FenceState { status: Status::Signaled, timestamp_ns });
133 } else {
134 state.push(FenceState { status: Status::Active, timestamp_ns: 0 });
135 }
136 }
137 state
138 }
139}
140
141impl FileOps for SyncFile {
142 fileops_impl_nonseekable!();
143 fileops_impl_noop_sync!();
144
145 fn to_handle(
146 &self,
147 _file: &FileObject,
148 _current_task: &CurrentTask,
149 ) -> Result<Option<zx::NullableHandle>, Errno> {
150 error!(ENOTSUP)
151 }
152
153 fn get_handles(
154 &self,
155 _file: &FileObject,
156 _current_task: &CurrentTask,
157 ) -> Result<Vec<zx::NullableHandle>, Errno> {
158 let mut handles = Vec::with_capacity(self.fence.sync_points.len());
159 for sync_point in &self.fence.sync_points {
160 let handle = sync_point
161 .counter
162 .duplicate_handle(zx::Rights::SAME_RIGHTS)
163 .map_err(impossible_error)?
164 .into();
165 handles.push(handle);
166 }
167 Ok(handles)
168 }
169
170 fn ioctl(
171 &self,
172 locked: &mut Locked<Unlocked>,
173 _file: &FileObject,
174 current_task: &CurrentTask,
175 request: u32,
176 arg: SyscallArg,
177 ) -> Result<SyscallResult, Errno> {
178 let user_addr = UserAddress::from(arg);
179 let ioctl_type = (request >> 8) as u8;
180 let ioctl_number = request as u8;
181
182 if ioctl_type != SYNC_IOC_MAGIC {
183 log_warn!("Unexpected type {:?}", ioctl_type);
184 return error!(EINVAL);
185 }
186
187 match ioctl_number {
188 SYNC_IOC_MERGE => {
189 fuchsia_trace::duration!(TRACE_CATEGORY, "SyncFileMerge");
190 let user_ref = UserRef::new(user_addr);
191 let mut merge_data: sync_merge_data = current_task.read_object(user_ref)?;
192 let file2 = current_task.get_file(FdNumber::from_raw(merge_data.fd2))?;
193
194 let file2_sync = file2.downcast_file::<SyncFile>();
195 let max_capacity = self.fence.sync_points.len()
196 + if let Some(file2) = file2_sync {
197 file2.fence.sync_points.len()
198 } else {
199 1
201 };
202
203 let mut fence = SyncFence { sync_points: Vec::with_capacity(max_capacity) };
204 let mut set = HashSet::<zx::Koid>::with_capacity(max_capacity);
205
206 for sync_point in &self.fence.sync_points {
207 let koid = sync_point.koid();
208 if set.insert(koid) {
209 fence.sync_points.push(sync_point.clone());
210 }
211 }
212
213 if let Some(file2) = file2_sync {
214 for sync_point in &file2.fence.sync_points {
215 let koid = sync_point.koid();
216 if set.insert(koid) {
217 fence.sync_points.push(sync_point.clone());
218 }
219 }
220 } else if let Some(file2) = file2.downcast_file::<RemoteCounter>() {
221 let counter = file2.duplicate_handle()?;
222 let sp = SyncPoint::with_koid(Timeline::Hwc, counter.into(), file2.koid());
223 if set.insert(sp.koid()) {
224 fence.sync_points.push(sp);
225 }
226 } else {
227 return error!(EINVAL);
228 }
229
230 let mut last_signaled_timestamp_ns = 0;
232 let mut last_signaled_sync_point: Option<SyncPoint> = None;
233
234 fence.sync_points.retain(|sync_point| {
235 let timestamp_ns = match sync_point.counter.read() {
236 Ok(t) if t > 0 => t as u64,
237 _ => return true, };
239
240 if timestamp_ns >= last_signaled_timestamp_ns {
242 last_signaled_timestamp_ns = timestamp_ns;
243 last_signaled_sync_point = Some(sync_point.clone());
244 }
245 false });
247
248 if fence.sync_points.is_empty() {
249 fence.sync_points.push(last_signaled_sync_point.expect("No sync points left."));
250 }
251
252 let name = merge_data.name.map(|x| x as u8);
253 let file = SyncFile::new_file(locked, current_task, name, fence)?;
254
255 let fd = current_task.add_file(locked, file, FdFlags::empty())?;
256 merge_data.fence = fd.raw();
257
258 current_task.write_object(user_ref, &merge_data)?;
259 Ok(SUCCESS)
260 }
261 SYNC_IOC_FILE_INFO => {
262 fuchsia_trace::duration!(TRACE_CATEGORY, "SyncFileInfo");
263 let user_ref = UserRef::new(user_addr);
264 let mut info: sync_file_info = current_task.read_object(user_ref)?;
265
266 for i in 0..self.name.len() {
267 info.name[i] = self.name[i] as c_char;
268 }
269 info.status = 0;
270
271 if info.num_fences == 0 {
272 info.num_fences = self.fence.sync_points.len() as u32;
273 } else if info.num_fences > self.fence.sync_points.len() as u32 {
274 return error!(EINVAL);
275 } else {
276 let fence_state = self.get_fence_state();
277 let mut user_addr = info.sync_fence_info;
278
279 let mut sync_file_status = 1;
280 for (i, state) in fence_state.iter().enumerate() {
281 if state.status == Status::Active {
282 sync_file_status = 0;
283 }
284 if i < info.num_fences as usize {
285 let mut fence_info = sync_fence_info {
287 status: state.status as i32,
288 timestamp_ns: state.timestamp_ns,
289 ..sync_fence_info::default()
290 };
291 let driver_name = match self.fence.sync_points[i].timeline {
292 Timeline::Magma => b"Magma\0",
293 Timeline::Hwc => b"Hwc\0\0\0",
294 };
295 assert!(driver_name.len() <= fence_info.driver_name.len());
296 for i in 0..driver_name.len() {
297 fence_info.driver_name[i] = driver_name[i] as c_char;
298 }
299
300 let fence_user_ref = UserRef::new(UserAddress::from(user_addr));
301 user_addr += std::mem::size_of::<sync_fence_info>() as u64;
302
303 current_task.write_object(fence_user_ref, &fence_info)?;
304 }
305 }
306
307 info.status = sync_file_status;
308 }
309
310 current_task.write_object(user_ref, &info)?;
311 Ok(SUCCESS)
312 }
313 _ => {
314 error!(EINVAL)
315 }
316 }
317 }
318
319 fn wait_async(
320 &self,
321 _locked: &mut Locked<FileOpsCore>,
322 _file: &FileObject,
323 _current_task: &CurrentTask,
324 waiter: &Waiter,
325 events: FdEvents,
326 event_handler: EventHandler,
327 ) -> Option<WaitCanceler> {
328 if !events.contains(FdEvents::POLLIN) {
329 return None;
330 }
331
332 let count = Arc::<AtomicCounter<usize>>::new(0.into());
333
334 let mut canceler = WaitCanceler::new_noop();
335
336 for sync_point in &self.fence.sync_points {
337 let signal_handler = SignalHandler {
338 inner: SignalHandlerInner::ManyZxHandle(ManyZxHandleSignalHandler {
339 count: self.fence.sync_points.len(),
340 counter: count.clone(),
341 expected_signals: Self::SIGNALS,
342 events: FdEvents::POLLIN,
343 }),
344 event_handler: event_handler.clone(),
345 err_code: None,
346 };
347
348 let canceler_result = waiter.wake_on_zircon_signals(
349 sync_point.counter.as_ref(),
350 Self::SIGNALS,
351 signal_handler,
352 );
353 let canceler_result = match canceler_result {
354 Ok(o) => o,
355 Err(e) => {
356 log_warn!("Error returned from wake_on_zircon_signals: {:?}", e);
357 return None;
358 }
359 };
360
361 if sync_point.counter.wait_one(Self::SIGNALS, zx::MonotonicInstant::ZERO).to_result()
367 == Err(zx::Status::TIMED_OUT)
368 {
369 canceler = WaitCanceler::merge_unbounded(
370 canceler,
371 WaitCanceler::new_port(canceler_result),
372 );
373 } else {
374 canceler_result.cancel();
375 count.next();
376 }
377 }
378
379 Some(canceler)
380 }
381
382 fn query_events(
383 &self,
384 _locked: &mut Locked<FileOpsCore>,
385 _file: &FileObject,
386 _current_task: &CurrentTask,
387 ) -> Result<FdEvents, Errno> {
388 let fence_state = self.get_fence_state();
389
390 for state in fence_state.iter() {
391 if state.status == Status::Active {
392 return Ok(FdEvents::empty());
393 }
394 }
395
396 Ok(FdEvents::POLLIN)
397 }
398
399 fn read(
400 &self,
401 _locked: &mut Locked<FileOpsCore>,
402 _file: &FileObject,
403 _current_task: &CurrentTask,
404 _offset: usize,
405 _data: &mut dyn OutputBuffer,
406 ) -> Result<usize, Errno> {
407 error!(ENODEV)
408 }
409
410 fn write(
411 &self,
412 _locked: &mut Locked<FileOpsCore>,
413 _file: &FileObject,
414 _current_task: &CurrentTask,
415 _offset: usize,
416 _data: &mut dyn InputBuffer,
417 ) -> Result<usize, Errno> {
418 error!(ENODEV)
419 }
420}
421
422#[cfg(test)]
423mod test {
424 use super::*;
425 use crate::mm::PAGE_SIZE;
426 use crate::testing::*;
427 use crate::vfs::{FdFlags, FdNumber};
428 use starnix_uapi::sync_merge_data;
429 use starnix_uapi::user_address::UserRef;
430
431 #[::fuchsia::test]
432 async fn test_sync_file_merge() {
433 spawn_kernel_and_run(async |locked, current_task| {
434 let counter1 = zx::Counter::create();
435 let counter2 = zx::Counter::create();
436
437 let sp1 = SyncPoint::new(Timeline::Magma, counter1);
438 let sp2 = SyncPoint::new(Timeline::Hwc, counter2);
439
440 let file1 = SyncFile::new_file(
441 locked,
442 ¤t_task,
443 [0; 32],
444 SyncFence { sync_points: vec![sp1.clone()] },
445 )
446 .unwrap();
447 let file2 = SyncFile::new_file(
448 locked,
449 ¤t_task,
450 [0; 32],
451 SyncFence { sync_points: vec![sp2.clone()] },
452 )
453 .unwrap();
454
455 let fd2 = current_task.add_file(locked, file2, FdFlags::empty()).unwrap();
456
457 let merge_data =
458 sync_merge_data { name: [0; 32], fd2: fd2.raw(), fence: 0, flags: 0, pad: 0 };
459
460 let user_addr = map_memory(locked, ¤t_task, UserAddress::default(), *PAGE_SIZE);
461 current_task.write_object(UserRef::new(user_addr), &merge_data).unwrap();
462
463 let request = ((SYNC_IOC_MAGIC as u32) << 8) | (SYNC_IOC_MERGE as u32);
464
465 let sync_file1 = file1.downcast_file::<SyncFile>().unwrap();
466 let res =
467 sync_file1.ioctl(locked, &file1, ¤t_task, request, user_addr.into()).unwrap();
468 assert_eq!(res, SUCCESS);
469
470 let updated_merge_data: sync_merge_data =
471 current_task.read_object(UserRef::new(user_addr)).unwrap();
472 let new_fd = FdNumber::from_raw(updated_merge_data.fence);
473
474 let new_file = current_task.get_file(new_fd).unwrap();
475 let merged_sync_file = new_file.downcast_file::<SyncFile>().unwrap();
476
477 assert_eq!(merged_sync_file.fence.sync_points.len(), 2);
478 assert_eq!(merged_sync_file.fence.sync_points[0].koid(), sp1.koid());
479 assert_eq!(merged_sync_file.fence.sync_points[1].koid(), sp2.koid());
480 })
481 .await;
482 }
483}