Claude net/fs review
This commit is contained in:
@@ -24,6 +24,10 @@ enum CompletionKind {
|
||||
NotifySend = 3,
|
||||
Operation = 4,
|
||||
OperationCancel = 5,
|
||||
/// CQE produced by the IORING_OP_LINK_TIMEOUT SQE that accompanies a
|
||||
/// linked operation. We register it so the token range is claimed, but
|
||||
/// the completion itself carries no useful information and is discarded.
|
||||
LinkedTimeout = 6,
|
||||
}
|
||||
|
||||
type CompletionHandler = Box<dyn FnOnce(IoUringCqe) + Send + 'static>;
|
||||
@@ -224,6 +228,49 @@ impl Driver {
|
||||
Ok(token)
|
||||
}
|
||||
|
||||
/// Submits a main operation linked to a timeout.
|
||||
///
|
||||
/// Internally two SQEs are enqueued atomically: the main op (with
|
||||
/// `IOSQE_IO_LINK`) and an `IORING_OP_LINK_TIMEOUT` SQE. If `timeout`
|
||||
/// elapses before the main op completes, the kernel cancels the main op and
|
||||
/// its CQE will carry `-ECANCELED`. The timeout's own CQE is silently
|
||||
/// discarded in `process_cqe`.
|
||||
///
|
||||
/// Returns the token for the main operation, which can be used with
|
||||
/// `cancel_operation` to cancel it early.
|
||||
pub(crate) fn submit_operation_with_linked_timeout(
|
||||
&self,
|
||||
fill: impl FnOnce(&mut IoUringSqe),
|
||||
timeout: Duration,
|
||||
on_complete: impl FnOnce(IoUringCqe) + Send + 'static,
|
||||
) -> io::Result<u64> {
|
||||
let main_token = self.next_token(CompletionKind::Operation);
|
||||
let timeout_token = self.next_token(CompletionKind::LinkedTimeout);
|
||||
#[cfg(debug_assertions)]
|
||||
tracing::trace!(
|
||||
target: trace_targets::ASYNC,
|
||||
event = "submit_operation_with_linked_timeout",
|
||||
main_token,
|
||||
timeout_token,
|
||||
timeout_ns = timeout.as_nanos() as u64,
|
||||
"submitting async driver operation with linked timeout"
|
||||
);
|
||||
self.completions
|
||||
.borrow_mut()
|
||||
.insert(main_token, Box::new(on_complete));
|
||||
|
||||
if let Err(error) =
|
||||
self.ring
|
||||
.submit_linked_with_timeout(main_token, fill, timeout_token, timeout)
|
||||
{
|
||||
let mut completions = self.completions.borrow_mut();
|
||||
let _ = completions.remove(&main_token);
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
Ok(main_token)
|
||||
}
|
||||
|
||||
pub(crate) fn cancel_operation(&self, token: u64) -> io::Result<()> {
|
||||
#[cfg(debug_assertions)]
|
||||
tracing::trace!(
|
||||
@@ -302,6 +349,7 @@ impl Driver {
|
||||
Some(CompletionKind::TimerRemove)
|
||||
| Some(CompletionKind::NotifySend)
|
||||
| Some(CompletionKind::OperationCancel)
|
||||
| Some(CompletionKind::LinkedTimeout)
|
||||
| None => {}
|
||||
}
|
||||
}
|
||||
@@ -347,6 +395,7 @@ fn decode_token_kind(token: u64) -> Option<CompletionKind> {
|
||||
3 => Some(CompletionKind::NotifySend),
|
||||
4 => Some(CompletionKind::Operation),
|
||||
5 => Some(CompletionKind::OperationCancel),
|
||||
6 => Some(CompletionKind::LinkedTimeout),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,10 @@ const IORING_FEAT_SINGLE_MMAP: u32 = 1 << 0;
|
||||
|
||||
pub(crate) const IORING_OP_FSYNC: u8 = 3;
|
||||
pub(crate) const IORING_OP_POLL_ADD: u8 = 6;
|
||||
pub(crate) const IORING_OP_SENDMSG: u8 = 9;
|
||||
pub(crate) const IORING_OP_RECVMSG: u8 = 10;
|
||||
pub(crate) const IORING_OP_TIMEOUT: u8 = 11;
|
||||
pub(crate) const IORING_OP_LINK_TIMEOUT: u8 = 15;
|
||||
pub(crate) const IORING_OP_TIMEOUT_REMOVE: u8 = 12;
|
||||
pub(crate) const IORING_OP_ACCEPT: u8 = 13;
|
||||
pub(crate) const IORING_OP_ASYNC_CANCEL: u8 = 14;
|
||||
@@ -43,6 +46,7 @@ const IORING_MSG_DATA: u64 = 0;
|
||||
pub(crate) const IORING_FSYNC_DATASYNC: u32 = 1 << 0;
|
||||
pub(crate) const IORING_TIMEOUT_ABS: u32 = 1 << 0;
|
||||
pub(crate) const IORING_TIMEOUT_UPDATE: u32 = 1 << 1;
|
||||
pub(crate) const IOSQE_IO_LINK: u8 = 1 << 2;
|
||||
pub(crate) const IOSQE_CQE_SKIP_SUCCESS: u8 = 1 << 6;
|
||||
|
||||
thread_local! {
|
||||
@@ -328,6 +332,44 @@ impl IoUring {
|
||||
self.submit_pending().map(|_| ())
|
||||
}
|
||||
|
||||
/// Submits a main SQE linked to an `IORING_OP_LINK_TIMEOUT` SQE.
|
||||
///
|
||||
/// The main SQE is submitted with `IOSQE_IO_LINK` set so that if `timeout`
|
||||
/// elapses before the main op completes, the kernel cancels the main op
|
||||
/// (`-ECANCELED`) and the timeout CQE arrives with `-ETIME`. If the main op
|
||||
/// completes first, the timeout CQE arrives with `-ECANCELED`.
|
||||
///
|
||||
/// # Safety of the stack-allocated timespec
|
||||
///
|
||||
/// `KernelTimespec` is stack-allocated and its address is passed to the kernel
|
||||
/// in the timeout SQE. This is safe for the same reason as `submit_timeout`:
|
||||
/// both SQEs are flushed via the synchronous `io_uring_enter` call inside
|
||||
/// `submit_pending` before this function returns. The kernel copies the
|
||||
/// timespec value at submission time; no lifetime extension beyond this call
|
||||
/// is required.
|
||||
pub(crate) fn submit_linked_with_timeout(
|
||||
&self,
|
||||
main_token: u64,
|
||||
fill: impl FnOnce(&mut IoUringSqe),
|
||||
timeout_token: u64,
|
||||
timeout: Duration,
|
||||
) -> io::Result<()> {
|
||||
let timespec = duration_to_kernel_timespec(timeout);
|
||||
self.push_sqe(|sqe| {
|
||||
fill(sqe);
|
||||
sqe.flags |= IOSQE_IO_LINK;
|
||||
sqe.user_data = main_token;
|
||||
})?;
|
||||
self.push_sqe(|sqe| {
|
||||
sqe.opcode = IORING_OP_LINK_TIMEOUT;
|
||||
sqe.fd = -1;
|
||||
sqe.addr = (×pec as *const KernelTimespec) as u64;
|
||||
sqe.len = 1;
|
||||
sqe.user_data = timeout_token;
|
||||
})?;
|
||||
self.submit_pending().map(|_| ())
|
||||
}
|
||||
|
||||
pub(crate) fn drain_completions(&self, mut f: impl FnMut(IoUringCqe)) -> bool {
|
||||
let mut head = load_u32(self.cq_head);
|
||||
let tail = load_u32(self.cq_tail);
|
||||
|
||||
Reference in New Issue
Block a user