1use crate::mm::{
6 DesiredAddress, IOVecPtr, MappingName, MappingOptions, MemoryAccessorExt, MemoryManager,
7 ProtectionFlags, TaskMemoryAccessor,
8};
9use crate::task::dynamic_thread_spawner::SpawnRequestBuilder;
10use crate::task::{CurrentTask, SimpleWaiter, Task, TaskFlags, WaitQueue};
11use crate::vfs::eventfd::EventFdFileObject;
12use crate::vfs::syscalls::IocbPtr;
13use crate::vfs::{
14 FdNumber, FileHandle, InputBuffer, UserBuffersInputBuffer, VecInputBuffer, VecOutputBuffer,
15 WeakFileHandle, checked_add_offset_and_length,
16};
17use smallvec::smallvec;
18use starnix_logging::track_stub;
19use starnix_sync::{AioEventsLock, AioPendingOperationsLock, InterruptibleEvent, LockDepMutex};
20use starnix_syscalls::SyscallResult;
21use starnix_types::user_buffer::{UserBuffer, UserBuffers};
22use starnix_uapi::errors::{EINTR, ETIMEDOUT, Errno};
23use starnix_uapi::{
24 IOCB_CMD_PREAD, IOCB_CMD_PREADV, IOCB_CMD_PWRITE, IOCB_CMD_PWRITEV, IOCB_FLAG_RESFD,
25 aio_context_t, errno, error, io_event, iocb,
26};
27use std::collections::VecDeque;
28use std::sync::{Arc, OnceLock, Weak};
29use zerocopy::IntoBytes;
30
31const AIO_RING_SIZE: usize = 32;
33
34pub struct AioContext {
37 inner: Arc<AioContextInner>,
38}
39
40impl AioContext {
41 pub fn create(
42 current_task: &CurrentTask,
43 max_operations: usize,
44 ) -> Result<aio_context_t, Errno> {
45 let mm = current_task.mm()?;
46 let inner = AioContextInner::new(max_operations);
47 inner.spawn_worker_for(current_task, WorkerType::Read);
48 inner.spawn_worker_for(current_task, WorkerType::Write);
49 let context = Arc::new(AioContext { inner });
50 let context_addr = mm.map_anonymous(
51 DesiredAddress::Any,
52 AIO_RING_SIZE,
53 ProtectionFlags::READ | ProtectionFlags::WRITE,
54 MappingOptions::ANONYMOUS | MappingOptions::DONT_EXPAND,
55 MappingName::AioContext(context),
56 )?;
57 Ok(context_addr.ptr() as aio_context_t)
58 }
59
60 pub fn get_events(
61 &self,
62 current_task: &CurrentTask,
63 min_results: usize,
64 max_results: usize,
65 deadline: zx::MonotonicInstant,
66 ) -> Result<Vec<io_event>, Errno> {
67 self.inner.get_events(current_task, min_results, max_results, deadline)
68 }
69
70 pub fn submit(
71 self: &Arc<Self>,
72 current_task: &CurrentTask,
73 control_block: iocb,
74 iocb_addr: IocbPtr,
75 ) -> Result<(), Errno> {
76 self.inner.submit(current_task, control_block, iocb_addr)
77 }
78
79 pub fn cancel(
80 self: &Arc<Self>,
81 _current_task: &CurrentTask,
82 control_block: iocb,
83 iocb_addr: IocbPtr,
84 ) -> Result<(), Errno> {
85 self.inner.cancel(control_block, iocb_addr)
86 }
87}
88
89impl std::fmt::Debug for AioContext {
90 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91 f.debug_struct("AioContext").finish()
92 }
93}
94
95impl std::cmp::PartialEq for AioContext {
96 fn eq(&self, other: &AioContext) -> bool {
97 Arc::ptr_eq(&self.inner, &other.inner)
98 }
99}
100
101impl std::cmp::Eq for AioContext {}
102
103impl Drop for AioContext {
104 fn drop(&mut self) {
105 self.inner.stop();
106 }
107}
108
109struct WorkerCompletion {
110 thread_id: OnceLock<std::thread::ThreadId>,
111 done: zx::Event,
112}
113
114impl Default for WorkerCompletion {
115 fn default() -> Self {
116 Self { thread_id: OnceLock::new(), done: zx::Event::create() }
117 }
118}
119
120impl WorkerCompletion {
121 fn record_current_thread(&self) {
122 self.thread_id
123 .set(std::thread::current().id())
124 .expect("worker thread id should only be set once");
125 }
126
127 fn is_current_thread(&self) -> bool {
128 self.thread_id.get() == Some(&std::thread::current().id())
129 }
130
131 fn signal(&self) {
132 let _ = self.done.signal(zx::Signals::NONE, zx::Signals::USER_0);
133 }
134
135 fn wait(&self) {
136 if !self.is_current_thread() {
145 let _ = self.done.wait_one(zx::Signals::USER_0, zx::MonotonicInstant::INFINITE);
146 }
147 }
148}
149
150struct AioContextInner {
151 operations: OperationQueue,
152 results: ResultQueue,
153 read_worker: WorkerCompletion,
154 write_worker: WorkerCompletion,
155}
156
157impl AioContextInner {
158 fn new(max_operations: usize) -> Arc<Self> {
159 Arc::new(Self {
160 operations: OperationQueue::new(max_operations),
161 results: Default::default(),
162 read_worker: Default::default(),
163 write_worker: Default::default(),
164 })
165 }
166
167 fn worker_for(&self, worker_type: WorkerType) -> &WorkerCompletion {
168 match worker_type {
169 WorkerType::Read => &self.read_worker,
170 WorkerType::Write => &self.write_worker,
171 }
172 }
173
174 fn stop(&self) {
175 self.operations.stop();
176 self.write_worker.wait();
177 self.read_worker.wait();
178 }
179
180 fn get_events(
181 &self,
182 current_task: &CurrentTask,
183 min_results: usize,
184 max_results: usize,
185 deadline: zx::MonotonicInstant,
186 ) -> Result<Vec<io_event>, Errno> {
187 let mut events = self.results.dequeue(max_results);
188 if events.len() >= min_results {
189 return Ok(events);
190 }
191 let event = InterruptibleEvent::new();
192 loop {
193 let (mut waiter, guard) = SimpleWaiter::new(&event);
194 self.results.waiters.wait_async_simple(&mut waiter);
195 events.extend(self.results.dequeue(max_results - events.len()));
196 if events.len() >= min_results {
197 return Ok(events);
198 }
199 match current_task.block_until(guard, deadline) {
200 Err(err) if err == ETIMEDOUT => {
201 return Ok(events);
202 }
203 Err(err) if err == EINTR => {
204 if events.is_empty() {
205 Err(err)
206 } else {
207 return Ok(events);
208 }
209 }
210 result => result,
211 }?;
212 }
213 }
214
215 fn submit(
216 self: &Arc<Self>,
217 current_task: &CurrentTask,
218 control_block: iocb,
219 iocb_addr: IocbPtr,
220 ) -> Result<(), Errno> {
221 let op = IoOperation::new(current_task, control_block, iocb_addr)?;
222 self.operations.enqueue(op)
223 }
224
225 fn cancel(self: &Arc<Self>, control_block: iocb, iocb_addr: IocbPtr) -> Result<(), Errno> {
226 let op_type: OpType = (control_block.aio_lio_opcode as u32).try_into()?;
227 self.operations.remove(op_type.worker_type(), iocb_addr)
228 }
229
230 fn spawn_worker_for(self: &Arc<Self>, current_task: &CurrentTask, worker_type: WorkerType) {
231 let creds = current_task.current_creds().clone();
232 let inner = self.clone();
233 let closure = move |current_task: &CurrentTask| {
234 let worker = inner.worker_for(worker_type);
235 worker.record_current_thread();
236 let _guard = scopeguard::guard((), |_| {
237 if matches!(worker_type, WorkerType::Read) {
238 current_task.write().set_flags(TaskFlags::WORK_CANCELLED, false);
239 }
240 worker.signal();
241 });
242 current_task
243 .override_creds(creds, || inner.perform_next_action(current_task, worker_type))
244 };
245 let req = SpawnRequestBuilder::new()
246 .with_debug_name("aio-worker")
247 .with_sync_closure(closure)
248 .build();
249 current_task.kernel().kthreads.spawner().spawn_from_request(req);
250 }
251
252 fn perform_next_action(&self, current_task: &CurrentTask, worker_type: WorkerType) {
253 while let Ok(IoAction::Op(mut op)) =
254 self.operations.block_until_dequeue(current_task, worker_type)
255 {
256 let result = op.execute(current_task);
257 if matches!(worker_type, WorkerType::Read) {
258 self.operations.finish_active_read_op(current_task);
263 }
264 if let Some(result) = result {
265 self.results.enqueue(op.complete(result));
266
267 if let Some(eventfd) = op.eventfd.as_ref().and_then(Weak::upgrade) {
268 let mut input_buffer = VecInputBuffer::new(1u64.as_bytes());
269 let _ = eventfd.write(current_task, &mut input_buffer);
270 }
271 }
272 drop(op);
273 current_task.trigger_delayed_releaser();
275 }
276 }
277}
278
279#[derive(Debug, Clone, Copy)]
280enum WorkerType {
281 Read,
282 Write,
283}
284
285#[derive(Debug, Clone, Copy)]
286enum OpType {
287 PRead,
288 PReadV,
289 PWrite,
294 PWriteV,
295}
296
297impl OpType {
298 fn worker_type(self) -> WorkerType {
299 match self {
300 OpType::PRead | OpType::PReadV => WorkerType::Read,
301 OpType::PWrite | OpType::PWriteV => WorkerType::Write,
302 }
303 }
304}
305
306impl TryFrom<u32> for OpType {
307 type Error = Errno;
308
309 fn try_from(opcode: u32) -> Result<Self, Self::Error> {
310 match opcode {
311 IOCB_CMD_PREAD => Ok(Self::PRead),
312 IOCB_CMD_PREADV => Ok(Self::PReadV),
313 IOCB_CMD_PWRITE => Ok(Self::PWrite),
314 IOCB_CMD_PWRITEV => Ok(Self::PWriteV),
315 _ => {
316 track_stub!(TODO("https://fxbug.dev/297433877"), "io_submit opcode", opcode);
317 return error!(ENOSYS);
318 }
319 }
320 }
321}
322
323enum IoOperationType {
324 Read { file: WeakFileHandle, mm: Weak<MemoryManager>, buffers: UserBuffers },
325 Write { file: FileHandle, buffer: Result<VecInputBuffer, Errno> },
326}
327
328struct IoOperation {
329 op: IoOperationType,
330 offset: usize,
331 id: u64,
332 iocb_addr: IocbPtr,
333 eventfd: Option<WeakFileHandle>,
334}
335
336impl IoOperation {
337 fn new(
338 current_task: &CurrentTask,
339 control_block: iocb,
340 iocb_addr: IocbPtr,
341 ) -> Result<Self, Errno> {
342 if control_block.aio_reserved2 != 0 {
343 return error!(EINVAL);
344 }
345 let file = current_task.files().get(FdNumber::from_raw(control_block.aio_fildes as i32))?;
346 let op_type = (control_block.aio_lio_opcode as u32).try_into()?;
347 let offset = control_block.aio_offset.try_into().map_err(|_| errno!(EINVAL))?;
348 let flags = control_block.aio_flags;
349
350 match op_type {
351 OpType::PRead | OpType::PReadV => {
352 if !file.can_read() {
353 return error!(EBADF);
354 }
355 }
356 OpType::PWrite | OpType::PWriteV => {
357 if !file.can_write() {
358 return error!(EBADF);
359 }
360 }
361 }
362 let mut buffers = match op_type {
363 OpType::PRead | OpType::PWrite => smallvec![UserBuffer {
364 address: control_block.aio_buf.into(),
365 length: control_block.aio_nbytes as usize,
366 }],
367 OpType::PReadV | OpType::PWriteV => {
368 let iovec_addr = IOVecPtr::new(current_task, control_block.aio_buf);
369 let count: i32 = control_block.aio_nbytes.try_into().map_err(|_| errno!(EINVAL))?;
370 current_task.read_iovec(iovec_addr, count.into())?
371 }
372 };
373
374 let buffer_length = UserBuffer::cap_buffers_to_max_rw_count(
376 current_task.maximum_valid_address().ok_or_else(|| errno!(EINVAL))?,
377 &mut buffers,
378 )?;
379 checked_add_offset_and_length(offset, buffer_length)?;
380
381 let eventfd = if flags & IOCB_FLAG_RESFD != 0 {
382 let eventfd =
383 current_task.files().get(FdNumber::from_raw(control_block.aio_resfd as i32))?;
384 if eventfd.downcast_file::<EventFdFileObject>().is_none() {
385 return error!(EINVAL);
386 }
387 Some(Arc::downgrade(&eventfd))
388 } else {
389 None
390 };
391
392 let op = match op_type {
393 OpType::PRead | OpType::PReadV => {
394 let mm = current_task.mm()?;
395 IoOperationType::Read {
396 file: Arc::downgrade(&file),
397 mm: Arc::downgrade(&mm),
398 buffers,
399 }
400 }
401 OpType::PWrite | OpType::PWriteV => {
402 let buffer = UserBuffersInputBuffer::syscall_new(current_task, buffers)
403 .and_then(|mut source| source.read_all())
404 .map(VecInputBuffer::from);
405 IoOperationType::Write { file, buffer }
406 }
407 };
408
409 Ok(IoOperation { op, offset, id: control_block.aio_data, iocb_addr, eventfd })
410 }
411
412 fn worker_type(&self) -> WorkerType {
413 match &self.op {
414 IoOperationType::Read { .. } => WorkerType::Read,
415 IoOperationType::Write { .. } => WorkerType::Write,
416 }
417 }
418
419 fn execute(&mut self, current_task: &CurrentTask) -> Option<Result<SyscallResult, Errno>> {
420 let offset = self.offset;
421 let result = match &mut self.op {
422 IoOperationType::Read { file, mm, buffers } => {
423 let Some(file) = file.upgrade() else {
424 return None;
427 };
428 Self::do_read(current_task, file, mm, buffers, offset).map(Into::into)
429 }
430 IoOperationType::Write { file, buffer } => match buffer {
431 Ok(input_buffer) => {
432 Self::do_write(current_task, file, offset, input_buffer).map(Into::into)
433 }
434 Err(errno) => Err(errno.clone()),
435 },
436 };
437 Some(result)
438 }
439
440 fn complete(&self, result: Result<SyscallResult, Errno>) -> io_event {
441 let res = match result {
442 Ok(return_value) => return_value.value() as i64,
443 Err(errno) => errno.return_value() as i64,
444 };
445
446 io_event { data: self.id, obj: self.iocb_addr.addr().into(), res, ..Default::default() }
447 }
448
449 fn do_read(
450 current_task: &CurrentTask,
451 file: FileHandle,
452 mm: &Weak<MemoryManager>,
453 buffers: &UserBuffers,
454 offset: usize,
455 ) -> Result<usize, Errno> {
456 let mut output_buffer = VecOutputBuffer::new(buffers.iter().map(|b| b.length).sum());
457
458 if file.ops().is_seekable() {
459 file.read_at(current_task, offset, &mut output_buffer)?;
460 } else {
461 file.read(current_task, &mut output_buffer)?;
462 }
463 drop(file);
464
465 let mm = mm.upgrade().ok_or_else(|| errno!(EFAULT))?;
466 let mut bytes = output_buffer.data();
467 let mut bytes_written = 0;
468 for buffer in buffers {
469 if buffer.is_null() {
470 continue;
471 }
472 let bytes_len = std::cmp::min(bytes.len(), buffer.length);
473 let (to_write, remaining) = bytes.split_at(bytes_len);
474 bytes = remaining;
475 bytes_written += mm.syscall_write_memory(buffer.address, to_write)?;
476 if bytes_len < buffer.length {
477 break;
478 }
479 }
480 Ok(bytes_written)
481 }
482
483 fn do_write(
484 current_task: &CurrentTask,
485 file: &FileHandle,
486 offset: usize,
487 input_buffer: &mut dyn InputBuffer,
488 ) -> Result<usize, Errno> {
489 if file.ops().is_seekable() {
490 file.write_at(current_task, offset, input_buffer)
491 } else {
492 file.write(current_task, input_buffer)
493 }
494 }
495}
496
497enum IoAction {
498 Op(IoOperation),
499 Stop,
500}
501
502#[derive(Default)]
503struct PendingOperations {
504 is_stopped: bool,
505 read_ops: VecDeque<IoOperation>,
509 write_ops: VecDeque<IoOperation>,
510 active_read_task: Weak<Task>,
511}
512
513impl PendingOperations {
514 fn ops_for(&mut self, worker_type: WorkerType) -> &mut VecDeque<IoOperation> {
515 match worker_type {
516 WorkerType::Read => &mut self.read_ops,
517 WorkerType::Write => &mut self.write_ops,
518 }
519 }
520
521 fn ops_len(&self) -> usize {
522 self.read_ops.len() + self.write_ops.len()
523 }
524}
525
526struct OperationQueue {
527 max_operations: usize,
528 pending: LockDepMutex<PendingOperations, AioPendingOperationsLock>,
529 read_waiters: WaitQueue,
530 write_waiters: WaitQueue,
531}
532
533impl OperationQueue {
534 fn new(max_operations: usize) -> Self {
535 Self {
536 max_operations,
537 pending: Default::default(),
538 read_waiters: Default::default(),
539 write_waiters: Default::default(),
540 }
541 }
542
543 fn waiters_for(&self, worker_type: WorkerType) -> &WaitQueue {
544 match worker_type {
545 WorkerType::Read => &self.read_waiters,
546 WorkerType::Write => &self.write_waiters,
547 }
548 }
549
550 fn enqueue(&self, op: IoOperation) -> Result<(), Errno> {
551 let worker_type = op.worker_type();
552 {
553 let mut pending = self.pending.lock();
554 if pending.is_stopped {
555 return error!(EINVAL);
556 }
557 if pending.ops_len() >= self.max_operations {
558 return error!(EAGAIN);
559 }
560 pending.ops_for(worker_type).push_back(op);
561 }
562 self.waiters_for(worker_type).notify_unordered_count(1);
563 Ok(())
564 }
565
566 fn stop(&self) {
567 {
568 let mut pending = self.pending.lock();
569 pending.is_stopped = true;
570 pending.read_ops.clear();
571 if let Some(task) = std::mem::take(&mut pending.active_read_task).upgrade() {
572 task.write().set_flags(TaskFlags::WORK_CANCELLED, true);
573 task.interrupt();
574 }
575 }
576 self.read_waiters.notify_all();
577 self.write_waiters.notify_all();
578 }
579
580 fn finish_active_read_op(&self, current_task: &CurrentTask) {
581 let mut pending = self.pending.lock();
582 pending.active_read_task = Default::default();
583 current_task.write().set_flags(TaskFlags::WORK_CANCELLED, false);
584 }
585
586 fn dequeue(&self, current_task: &CurrentTask, worker_type: WorkerType) -> Option<IoAction> {
587 let mut pending = self.pending.lock();
588 match worker_type {
589 WorkerType::Read => {
590 if pending.is_stopped {
591 return Some(IoAction::Stop);
592 }
593 if let Some(op) = pending.read_ops.pop_front() {
594 pending.active_read_task = current_task.weak_task();
595 Some(IoAction::Op(op))
596 } else {
597 None
598 }
599 }
600 WorkerType::Write => {
601 if let Some(op) = pending.write_ops.pop_front() {
602 return Some(IoAction::Op(op));
603 }
604 if pending.is_stopped {
605 return Some(IoAction::Stop);
606 }
607 None
608 }
609 }
610 }
611
612 fn remove(&self, worker_type: WorkerType, iocb_addr: IocbPtr) -> Result<(), Errno> {
613 {
614 let mut pending = self.pending.lock();
615 if pending.is_stopped {
616 return error!(EINVAL);
617 }
618 if let Some(idx) = pending
620 .ops_for(worker_type)
621 .iter()
622 .position(|value| value.iocb_addr.addr() == iocb_addr.addr())
623 {
624 let _ = pending.ops_for(worker_type).remove(idx);
625 } else {
626 return error!(EAGAIN);
627 }
628 }
629 Ok(())
630 }
631
632 fn block_until_dequeue(
633 &self,
634 current_task: &CurrentTask,
635 worker_type: WorkerType,
636 ) -> Result<IoAction, Errno> {
637 if let Some(action) = self.dequeue(current_task, worker_type) {
638 return Ok(action);
639 }
640 loop {
641 let event = InterruptibleEvent::new();
642 let (mut waiter, guard) = SimpleWaiter::new(&event);
643 self.waiters_for(worker_type).wait_async_simple(&mut waiter);
644 if let Some(action) = self.dequeue(current_task, worker_type) {
645 return Ok(action);
646 }
647 match current_task.block_until(guard, zx::MonotonicInstant::INFINITE) {
648 Err(err) if err == EINTR => continue,
649 result => result?,
650 }
651 }
652 }
653}
654
655#[derive(Default)]
656struct ResultQueue {
657 waiters: WaitQueue,
658 events: LockDepMutex<VecDeque<io_event>, AioEventsLock>,
659}
660
661impl ResultQueue {
662 fn enqueue(&self, event: io_event) {
663 self.events.lock().push_back(event);
664 self.waiters.notify_unordered_count(1);
665 }
666
667 fn dequeue(&self, limit: usize) -> Vec<io_event> {
668 let mut events = self.events.lock();
669 let len = std::cmp::min(events.len(), limit);
670 events.drain(..len).collect()
671 }
672}
673
674#[cfg(test)]
675mod tests {
676 use super::*;
677 use crate::mm::{MemoryAccessor, PAGE_SIZE};
678 use crate::task::Waiter;
679 use crate::testing::{map_memory, spawn_kernel_and_run};
680 use crate::vfs::{
681 Anon, FdFlags, FileObject, FileOps, OutputBuffer, fileops_impl_noop_sync,
682 fileops_impl_seekless,
683 };
684 use starnix_sync::Mutex;
685 use starnix_uapi::open_flags::OpenFlags;
686 use starnix_uapi::user_address::{UserAddress, UserRef};
687 use starnix_uapi::{CLONE_SIGHAND, CLONE_THREAD, CLONE_VM};
688
689 struct TestSinkFile {
690 written: Arc<Mutex<Vec<u8>>>,
691 }
692
693 impl FileOps for TestSinkFile {
694 fileops_impl_seekless!();
695 fileops_impl_noop_sync!();
696
697 fn read(
698 &self,
699 _file: &FileObject,
700 _current_task: &CurrentTask,
701 _offset: usize,
702 _data: &mut dyn OutputBuffer,
703 ) -> Result<usize, Errno> {
704 error!(EINVAL)
705 }
706
707 fn write(
708 &self,
709 _file: &FileObject,
710 _current_task: &CurrentTask,
711 _offset: usize,
712 data: &mut dyn InputBuffer,
713 ) -> Result<usize, Errno> {
714 let bytes = data.read_all()?;
715 let len = bytes.len();
716 self.written.lock().extend_from_slice(&bytes);
717 Ok(len)
718 }
719 }
720
721 struct TestBlockedReadFile {
722 read_started: Arc<zx::Event>,
723 file_released: Arc<zx::Event>,
724 }
725
726 impl Drop for TestBlockedReadFile {
727 fn drop(&mut self) {
728 let _ = self.file_released.signal(zx::Signals::NONE, zx::Signals::USER_0);
729 }
730 }
731
732 impl FileOps for TestBlockedReadFile {
733 fileops_impl_seekless!();
734 fileops_impl_noop_sync!();
735
736 fn read(
737 &self,
738 _file: &FileObject,
739 current_task: &CurrentTask,
740 _offset: usize,
741 _data: &mut dyn OutputBuffer,
742 ) -> Result<usize, Errno> {
743 let _ = self.read_started.signal(zx::Signals::NONE, zx::Signals::USER_0);
744 let waiter = Waiter::new();
745 waiter.wait(current_task)?;
746 unreachable!("blocked read should be interrupted on context drop");
747 }
748
749 fn write(
750 &self,
751 _file: &FileObject,
752 _current_task: &CurrentTask,
753 _offset: usize,
754 _data: &mut dyn InputBuffer,
755 ) -> Result<usize, Errno> {
756 error!(EINVAL)
757 }
758 }
759
760 struct TestGatedReadFile {
761 unblock: Arc<zx::Event>,
762 payload: Vec<u8>,
763 }
764
765 impl FileOps for TestGatedReadFile {
766 fileops_impl_seekless!();
767 fileops_impl_noop_sync!();
768
769 fn read(
770 &self,
771 _file: &FileObject,
772 _current_task: &CurrentTask,
773 _offset: usize,
774 data: &mut dyn OutputBuffer,
775 ) -> Result<usize, Errno> {
776 self.unblock
777 .wait_one(zx::Signals::USER_0, zx::MonotonicInstant::INFINITE)
778 .map_err(|_| errno!(EINTR))?;
779 data.write(&self.payload)
780 }
781
782 fn write(
783 &self,
784 _file: &FileObject,
785 _current_task: &CurrentTask,
786 _offset: usize,
787 _data: &mut dyn InputBuffer,
788 ) -> Result<usize, Errno> {
789 error!(EINVAL)
790 }
791 }
792
793 #[::fuchsia::test]
794 async fn test_pwrite_completes_after_fd_closed_and_buffer_unmapped() {
795 spawn_kernel_and_run(async |current_task| {
796 let header = b"A_WRTE_HEADER:";
797 let payload = b"restarting adbd as root\n";
798 let user_buf = map_memory(current_task, UserAddress::default(), *PAGE_SIZE);
799 current_task.write_memory(user_buf, header).expect("write header");
800 let payload_buf = (user_buf + header.len()).expect("payload addr");
801 current_task.write_memory(payload_buf, payload).expect("write user payload");
802
803 let iocb_addr1 = map_memory(current_task, UserAddress::default(), *PAGE_SIZE);
804 let iocb_ref1 = IocbPtr::new(current_task, iocb_addr1);
805 let iocb_addr2 = (iocb_addr1 + std::mem::size_of::<iocb>()).expect("iocb2 addr");
806 let iocb_ref2 = IocbPtr::new(current_task, iocb_addr2);
807
808 let written = Arc::new(Mutex::new(Vec::new()));
809 let write_file = Anon::new_private_file(
810 current_task,
811 Box::new(TestSinkFile { written: Arc::clone(&written) }),
812 OpenFlags::WRONLY,
813 "[test-sink]",
814 );
815 let write_fd = current_task.add_file(write_file, FdFlags::empty()).expect("add file");
816
817 let cb1 = iocb {
818 aio_fildes: write_fd.raw() as u32,
819 aio_lio_opcode: IOCB_CMD_PWRITE as u16,
820 aio_buf: user_buf.ptr() as u64,
821 aio_nbytes: header.len() as u64,
822 aio_offset: 0,
823 aio_data: 0x1234,
824 ..Default::default()
825 };
826 let cb2 = iocb {
827 aio_fildes: write_fd.raw() as u32,
828 aio_lio_opcode: IOCB_CMD_PWRITE as u16,
829 aio_buf: payload_buf.ptr() as u64,
830 aio_nbytes: payload.len() as u64,
831 aio_offset: 0,
832 aio_data: 0x1235,
833 ..Default::default()
834 };
835
836 let ctx_id = AioContext::create(current_task, 16).expect("create aio context");
837 let aio_ctx = current_task
838 .mm()
839 .expect("mm")
840 .get_aio_context(ctx_id.into())
841 .expect("get aio context");
842 aio_ctx.submit(current_task, cb1, iocb_ref1).expect("submit header op");
843 aio_ctx.submit(current_task, cb2, iocb_ref2).expect("submit payload op");
844
845 current_task.files().close(write_fd).expect("close write fd");
848 current_task
849 .mm()
850 .expect("mm")
851 .unmap(user_buf, (*PAGE_SIZE) as usize)
852 .expect("unmap user buf");
853 aio_ctx.inner.stop();
854
855 let mut expected = Vec::new();
856 expected.extend_from_slice(header);
857 expected.extend_from_slice(payload);
858 assert_eq!(written.lock().as_slice(), expected.as_slice());
859 })
860 .await;
861 }
862
863 #[::fuchsia::test]
864 async fn test_drop_cancels_blocked_read_and_releases_file() {
865 spawn_kernel_and_run(async |current_task| {
866 let user_buf = map_memory(current_task, UserAddress::default(), *PAGE_SIZE);
867 let iocb_addr = map_memory(current_task, UserAddress::default(), *PAGE_SIZE);
868 let iocb_ref = IocbPtr::new(current_task, iocb_addr);
869
870 let read_started = Arc::new(zx::Event::create());
871 let file_released = Arc::new(zx::Event::create());
872 let read_file = Anon::new_private_file(
873 current_task,
874 Box::new(TestBlockedReadFile {
875 read_started: Arc::clone(&read_started),
876 file_released: Arc::clone(&file_released),
877 }),
878 OpenFlags::RDONLY,
879 "[test-blocked-read]",
880 );
881 let weak_read_file = Arc::downgrade(&read_file);
882 let read_fd = current_task.add_file(read_file, FdFlags::empty()).expect("add file");
883
884 let cb = iocb {
885 aio_fildes: read_fd.raw() as u32,
886 aio_lio_opcode: IOCB_CMD_PREAD as u16,
887 aio_buf: user_buf.ptr() as u64,
888 aio_nbytes: 64,
889 aio_offset: 0,
890 aio_data: 0x5678,
891 ..Default::default()
892 };
893
894 let ctx_id = AioContext::create(current_task, 16).expect("create aio context");
895 let aio_ctx = current_task
896 .mm()
897 .expect("mm")
898 .get_aio_context(ctx_id.into())
899 .expect("get aio context");
900 aio_ctx.submit(current_task, cb, iocb_ref).expect("submit read op");
901 std::mem::drop(aio_ctx);
902
903 read_started
905 .wait_one(zx::Signals::USER_0, zx::MonotonicInstant::INFINITE)
906 .expect("read started");
907
908 current_task.files().close(read_fd).expect("close read fd");
909 current_task.trigger_delayed_releaser();
910 assert!(
911 weak_read_file.upgrade().is_some(),
912 "blocked read worker should hold strong FileHandle before context teardown"
913 );
914
915 let aio_ctx = current_task
916 .mm()
917 .expect("mm")
918 .destroy_aio_context(ctx_id.into())
919 .expect("destroy aio context");
920 std::mem::drop(aio_ctx);
921
922 file_released
923 .wait_one(zx::Signals::USER_0, zx::MonotonicInstant::INFINITE_PAST)
924 .expect("read file released synchronously before context drop returns");
925
926 assert!(
927 weak_read_file.upgrade().is_none(),
928 "tearing down AioContext must interrupt blocked read and release FileHandle"
929 );
930 })
931 .await;
932 }
933
934 #[::fuchsia::test]
935 async fn test_pread_completes_after_submitting_thread_exits() {
936 spawn_kernel_and_run(async |current_task| {
937 let payload = b"pread-after-submitting-thread-exit";
938 let user_buf = map_memory(current_task, UserAddress::default(), *PAGE_SIZE);
939 let iocb_addr = map_memory(current_task, UserAddress::default(), *PAGE_SIZE);
940 let iocb_ref = IocbPtr::new(current_task, iocb_addr);
941
942 let unblock = Arc::new(zx::Event::create());
943 let read_file = Anon::new_private_file(
944 current_task,
945 Box::new(TestGatedReadFile {
946 unblock: Arc::clone(&unblock),
947 payload: payload.to_vec(),
948 }),
949 OpenFlags::RDONLY,
950 "[test-gated-pread]",
951 );
952 let read_fd = current_task.add_file(read_file, FdFlags::empty()).expect("add file");
953
954 let cb = iocb {
955 aio_fildes: read_fd.raw() as u32,
956 aio_lio_opcode: IOCB_CMD_PREAD as u16,
957 aio_buf: user_buf.ptr() as u64,
958 aio_nbytes: payload.len() as u64,
959 aio_offset: 0,
960 aio_data: 0x9abc,
961 ..Default::default()
962 };
963
964 let ctx_id = AioContext::create(current_task, 16).expect("create aio context");
965 let aio_ctx = current_task
966 .mm()
967 .expect("mm")
968 .get_aio_context(ctx_id.into())
969 .expect("get aio context");
970
971 let submitting_thread = current_task
972 .clone_task_for_test((CLONE_THREAD | CLONE_VM | CLONE_SIGHAND) as u64, None);
973 let weak_submitting_task = Arc::downgrade(&submitting_thread.task);
974 aio_ctx.submit(&submitting_thread, cb, iocb_ref).expect("submit pread op");
975 std::mem::drop(submitting_thread);
976 assert!(
977 weak_submitting_task.upgrade().is_none(),
978 "submitting thread Task should be dropped before read completes"
979 );
980
981 unblock.signal(zx::Signals::NONE, zx::Signals::USER_0).expect("signal unblock");
982 let events = aio_ctx
983 .get_events(current_task, 1, 1, zx::MonotonicInstant::INFINITE)
984 .expect("get_events");
985 assert_eq!(events.len(), 1);
986 assert_eq!(events[0].data, 0x9abc);
987 assert_eq!(events[0].res, payload.len() as i64);
988
989 let read_bytes =
990 current_task.read_memory_to_vec(user_buf, payload.len()).expect("read user_buf");
991 assert_eq!(read_bytes.as_slice(), payload);
992 })
993 .await;
994 }
995
996 #[::fuchsia::test]
997 async fn test_preadv_completes_after_submitting_thread_exits() {
998 spawn_kernel_and_run(async |current_task| {
999 let part1 = b"preadv-part1:";
1000 let part2 = b"preadv-part2";
1001 let mut payload = Vec::new();
1002 payload.extend_from_slice(part1);
1003 payload.extend_from_slice(part2);
1004
1005 let part1_buf = map_memory(current_task, UserAddress::default(), *PAGE_SIZE);
1006 let part2_buf = (part1_buf + part1.len()).expect("part2_buf addr");
1007
1008 let iovec_addr = map_memory(current_task, UserAddress::default(), *PAGE_SIZE);
1009 let iovecs = [
1010 UserBuffer { address: part1_buf, length: part1.len() },
1011 UserBuffer { address: part2_buf, length: part2.len() },
1012 ];
1013 current_task
1014 .write_objects(UserRef::new(iovec_addr), &iovecs)
1015 .expect("write iovecs to user memory");
1016
1017 let iocb_addr = map_memory(current_task, UserAddress::default(), *PAGE_SIZE);
1018 let iocb_ref = IocbPtr::new(current_task, iocb_addr);
1019
1020 let unblock = Arc::new(zx::Event::create());
1021 let read_file = Anon::new_private_file(
1022 current_task,
1023 Box::new(TestGatedReadFile {
1024 unblock: Arc::clone(&unblock),
1025 payload: payload.clone(),
1026 }),
1027 OpenFlags::RDONLY,
1028 "[test-gated-preadv]",
1029 );
1030 let read_fd = current_task.add_file(read_file, FdFlags::empty()).expect("add file");
1031
1032 let cb = iocb {
1033 aio_fildes: read_fd.raw() as u32,
1034 aio_lio_opcode: IOCB_CMD_PREADV as u16,
1035 aio_buf: iovec_addr.ptr() as u64,
1036 aio_nbytes: iovecs.len() as u64,
1037 aio_offset: 0,
1038 aio_data: 0x9abd,
1039 ..Default::default()
1040 };
1041
1042 let ctx_id = AioContext::create(current_task, 16).expect("create aio context");
1043 let aio_ctx = current_task
1044 .mm()
1045 .expect("mm")
1046 .get_aio_context(ctx_id.into())
1047 .expect("get aio context");
1048
1049 let submitting_thread = current_task
1050 .clone_task_for_test((CLONE_THREAD | CLONE_VM | CLONE_SIGHAND) as u64, None);
1051 let weak_submitting_task = Arc::downgrade(&submitting_thread.task);
1052 aio_ctx.submit(&submitting_thread, cb, iocb_ref).expect("submit preadv op");
1053 std::mem::drop(submitting_thread);
1054 assert!(
1055 weak_submitting_task.upgrade().is_none(),
1056 "submitting thread Task should be dropped before read completes"
1057 );
1058
1059 unblock.signal(zx::Signals::NONE, zx::Signals::USER_0).expect("signal unblock");
1060 let events = aio_ctx
1061 .get_events(current_task, 1, 1, zx::MonotonicInstant::INFINITE)
1062 .expect("get_events");
1063 assert_eq!(events.len(), 1);
1064 assert_eq!(events[0].data, 0x9abd);
1065 assert_eq!(events[0].res, payload.len() as i64);
1066
1067 let read_part1 =
1068 current_task.read_memory_to_vec(part1_buf, part1.len()).expect("read part1_buf");
1069 assert_eq!(read_part1.as_slice(), part1);
1070
1071 let read_part2 =
1072 current_task.read_memory_to_vec(part2_buf, part2.len()).expect("read part2_buf");
1073 assert_eq!(read_part2.as_slice(), part2);
1074 })
1075 .await;
1076 }
1077}