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