Skip to main content

strat9_kernel/async_io/
syscall.rs

1//! Asynchronous I/O syscall handlers.
2//!
3//! Dispatches SQEs to VFS backends via `AsyncScheme`,
4//! drains completions, and blocks when `min_complete > 0`.
5
6use alloc::sync::Arc;
7use strat9_abi::data::AsyncRingLayout;
8
9use crate::{memory::UserSliceWrite, syscall::error::SyscallError};
10
11use super::{complete, dispatch, ring::RingError};
12
13#[derive(Debug, Clone, Copy)]
14pub struct AsyncRingMapping {
15    pub sq_base: u64,
16    pub cq_base: u64,
17    pub sq_size: u64,
18    pub cq_size: u64,
19    pub entries: u32,
20}
21
22fn require_owned_ring_by_id(ring_id: u64) -> Result<Arc<super::ring::Ring>, SyscallError> {
23    let task = crate::process::current_task_clone().ok_or(SyscallError::PermissionDenied)?;
24    let ring = super::ring::find_ring(ring_id).ok_or(SyscallError::BadHandle)?;
25
26    if ring.owner_pid != task.pid {
27        return Err(SyscallError::PermissionDenied);
28    }
29
30    Ok(ring)
31}
32
33/// SYS_ASYNC_SETUP(entries: u32, flags: u32) → ring_id: u64
34pub fn sys_async_setup(entries: u64, _flags: u64) -> Result<u64, SyscallError> {
35    let Some(task) = crate::process::current_task_clone() else {
36        return Err(SyscallError::PermissionDenied);
37    };
38    let pid = task.pid;
39    let entries = entries as u32;
40
41    super::ring::Ring::create(pid, entries).map_err(|e| match e {
42        RingError::Alloc => SyscallError::OutOfMemory,
43        RingError::NotFound => SyscallError::BadHandle,
44        RingError::TooManyRings => SyscallError::OutOfMemory,
45    })
46}
47
48/// SYS_ASYNC_MAP(ring_id: u64, out_ptr: *mut AsyncRingLayout) -> total_mapped_bytes: u64
49pub fn sys_async_map(ring_id: u64, out_ptr: u64) -> Result<u64, SyscallError> {
50    if out_ptr == 0 {
51        return Err(SyscallError::Fault);
52    }
53
54    let task = crate::process::current_task_clone().ok_or(SyscallError::PermissionDenied)?;
55    let ring = require_owned_ring_by_id(ring_id)?;
56
57    let mapping = ring
58        .map_into_process(&task.process.address_space_arc())
59        .map_err(|_| SyscallError::OutOfMemory)?;
60
61    let abi = AsyncRingLayout {
62        sq_base: mapping.sq_base,
63        cq_base: mapping.cq_base,
64        sq_size: mapping.sq_size,
65        cq_size: mapping.cq_size,
66        entries: mapping.entries,
67        _reserved: 0,
68    };
69
70    let out = UserSliceWrite::new(out_ptr, core::mem::size_of::<AsyncRingLayout>())?;
71    let bytes = unsafe {
72        core::slice::from_raw_parts(
73            &abi as *const AsyncRingLayout as *const u8,
74            core::mem::size_of::<AsyncRingLayout>(),
75        )
76    };
77    out.copy_from(bytes);
78
79    Ok(mapping.sq_size.saturating_add(mapping.cq_size))
80}
81
82/// SYS_ASYNC_ENTER(ring_id: u64, to_submit: u32, min_complete: u32, flags: u32) → completed: u64
83pub fn sys_async_enter(
84    ring_id: u64,
85    to_submit: u64,
86    min_complete: u64,
87    _flags: u64,
88) -> Result<u64, SyscallError> {
89    let ring = require_owned_ring_by_id(ring_id)?;
90
91    let to_submit = to_submit as u32;
92    let min_complete = min_complete as u32;
93
94    // dispatch submitted SQEs to backends
95    if to_submit > 0 {
96        dispatch::drain_submissions(ring_id, to_submit);
97    }
98
99    crate::hardware::storage::ahci::flush_deferred_async_read_completions(ring_id);
100
101    // Drain up to `min_complete` completions.  The caller wants at least
102    // that many, so if fewer are available we block below.  Passing  `min_complete` as the "max" argument to `drain_completions` is correct.
103    // We never need more than `min_complete` in one round and the blocking path will loop via `wait_until` if needed.
104    let mut completed = complete::drain_completions(ring_id, min_complete);
105
106    // If caller asked for more completions than available, block and wait
107    if completed == 0 && min_complete > 0 {
108        completed = ring.wq.wait_until(|| {
109            if ring.destroyed.load(core::sync::atomic::Ordering::Acquire) != 0 {
110                return Some(0);
111            }
112
113            crate::hardware::storage::ahci::flush_deferred_async_read_completions(ring_id);
114
115            let ready = complete::drain_completions(ring_id, min_complete);
116            if ready > 0 {
117                Some(ready)
118            } else {
119                None
120            }
121        });
122    }
123
124    Ok(completed as u64)
125}
126
127/// SYS_ASYNC_CANCEL(ring_id: u64, user_data: u64, flags: u32) → result: u64
128pub fn sys_async_cancel(ring_id: u64, _user_data: u64, _flags: u64) -> Result<u64, SyscallError> {
129    let _ = require_owned_ring_by_id(ring_id)?;
130
131    Err(SyscallError::NotImplemented)
132}
133
134/// SYS_ASYNC_DESTROY(ring_id: u64, flags: u32) → result: u64
135pub fn sys_async_destroy(ring_id: u64, _flags: u64) -> Result<u64, SyscallError> {
136    let _ = require_owned_ring_by_id(ring_id)?;
137
138    super::ring::destroy_ring(ring_id).map_err(|e| match e {
139        RingError::Alloc => SyscallError::OutOfMemory,
140        RingError::NotFound => SyscallError::BadHandle,
141        RingError::TooManyRings => SyscallError::InvalidArgument,
142    })?;
143
144    Ok(0)
145}