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