cros_async/sys/linux/
uring_executor.rs

1// Copyright 2020 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// TODO: Move this doc to one of the public APIs, it isn't io_uring specific.
6
7//! `UringReactor`
8//!
9//! ## Read/Write buffer management.
10//!
11//! There are two key issues managing asynchronous IO buffers in rust.
12//! 1) The kernel has a mutable reference to the memory until the completion is returned. Rust must
13//!    not have any references to it during that time.
14//! 2) The memory must remain valid as long as the kernel has a reference to it.
15//!
16//! ### The kernel's mutable borrow of the buffer
17//!
18//! Because the buffers used for read and write must be passed to the kernel for an unknown
19//! duration, the functions must maintain ownership of the memory.  The core of this problem is that
20//! the lifetime of the future isn't tied to the scope in which the kernel can modify the buffer the
21//! future has a reference to.  The buffer can be modified at any point from submission until the
22//! operation completes. The operation can't be synchronously canceled when the future is dropped,
23//! and Drop can't be used for safety guarantees. To ensure this never happens, only memory that
24//! implements `BackingMemory` is accepted.  For implementors of `BackingMemory` the mut borrow
25//! isn't an issue because those are already Ok with external modifications to the memory (Like a
26//! `VolatileSlice`).
27//!
28//! ### Buffer lifetime
29//!
30//! What if the kernel's reference to the buffer outlives the buffer itself?  This could happen if a
31//! read operation was submitted, then the memory is dropped.  To solve this, the executor takes an
32//! Arc to the backing memory. Vecs being read to are also wrapped in an Arc before being passed to
33//! the executor.  The executor holds the Arc and ensures all operations are complete before
34//! dropping it, that guarantees the memory is valid for the duration.
35//!
36//! The buffers _have_ to be on the heap. Because we don't have a way to cancel a future if it is
37//! dropped(can't rely on drop running), there is no way to ensure the kernel's buffer remains valid
38//! until the operation completes unless the executor holds an Arc to the memory on the heap.
39//!
40//! ## Using `Vec` for reads/writes.
41//!
42//! There is a convenience wrapper `VecIoWrapper` provided for fully owned vectors. This type
43//! ensures that only the kernel is allowed to access the `Vec` and wraps the the `Vec` in an Arc to
44//! ensure it lives long enough.
45
46use std::convert::TryInto;
47use std::ffi::CStr;
48use std::fs::File;
49use std::future::Future;
50use std::io;
51use std::mem;
52use std::mem::MaybeUninit;
53use std::os::unix::io::FromRawFd;
54use std::os::unix::io::RawFd;
55use std::pin::Pin;
56use std::sync::Arc;
57use std::sync::LazyLock;
58use std::sync::Weak;
59use std::task::Context;
60use std::task::Poll;
61use std::task::Waker;
62use std::thread;
63use std::thread::ThreadId;
64
65use base::trace;
66use base::warn;
67use base::AsRawDescriptor;
68use base::EventType;
69use base::IoBufMut;
70use base::RawDescriptor;
71use io_uring::URingAllowlist;
72use io_uring::URingContext;
73use io_uring::URingOperation;
74use remain::sorted;
75use slab::Slab;
76use sync::Mutex;
77use thiserror::Error as ThisError;
78
79use crate::common_executor::RawExecutor;
80use crate::common_executor::RawTaskHandle;
81use crate::common_executor::Reactor;
82use crate::mem::BackingMemory;
83use crate::waker::WakerToken;
84use crate::waker::WeakWake;
85use crate::AsyncError;
86use crate::AsyncResult;
87use crate::IoSource;
88use crate::MemRegion;
89use crate::TaskHandle;
90
91#[sorted]
92#[derive(Debug, ThisError)]
93pub enum Error {
94    /// Creating a context to wait on FDs failed.
95    #[error("Error creating the fd waiting context: {0}")]
96    CreatingContext(io_uring::Error),
97    /// Failed to discard a block
98    #[error("Failed to discard a block: {0}")]
99    Discard(base::Error),
100    /// Failed to copy the FD for the polling context.
101    #[error("Failed to copy the FD for the polling context: {0}")]
102    DuplicatingFd(base::Error),
103    /// Enabling a context faild.
104    #[error("Error enabling the URing context: {0}")]
105    EnablingContext(io_uring::Error),
106    /// The Executor is gone.
107    #[error("The executor is gone")]
108    ExecutorGone,
109    /// Invalid offset or length given for an iovec in backing memory.
110    #[error("Invalid offset/len for getting an iovec")]
111    InvalidOffset,
112    /// Invalid FD source specified.
113    #[error("Invalid source, FD not registered for use")]
114    InvalidSource,
115    /// Error doing the IO.
116    #[error("Error during IO: {0}")]
117    Io(io::Error),
118    /// Registering operation restrictions to a uring failed.
119    #[error("Error registering restrictions to the URing context: {0}")]
120    RegisteringURingRestriction(io_uring::Error),
121    /// Failed to remove the waker remove the polling context.
122    #[error("Error removing from the URing context: {0}")]
123    RemovingWaker(io_uring::Error),
124    /// Failed to submit the operation to the polling context.
125    #[error("Error adding to the URing context: {0}")]
126    SubmittingOp(io_uring::Error),
127    /// URingContext failure.
128    #[error("URingContext failure: {0}")]
129    URingContextError(io_uring::Error),
130    /// Failed to submit or wait for io_uring events.
131    #[error("URing::enter: {0}")]
132    URingEnter(io_uring::Error),
133}
134pub type Result<T> = std::result::Result<T, Error>;
135
136impl From<Error> for io::Error {
137    fn from(e: Error) -> Self {
138        use Error::*;
139        match e {
140            Discard(e) => e.into(),
141            DuplicatingFd(e) => e.into(),
142            ExecutorGone => io::Error::other(ExecutorGone),
143            InvalidOffset => io::Error::new(io::ErrorKind::InvalidInput, InvalidOffset),
144            InvalidSource => io::Error::new(io::ErrorKind::InvalidData, InvalidSource),
145            Io(e) => e,
146            CreatingContext(e) => e.into(),
147            RemovingWaker(e) => e.into(),
148            SubmittingOp(e) => e.into(),
149            URingContextError(e) => e.into(),
150            URingEnter(e) => e.into(),
151            EnablingContext(e) => e.into(),
152            RegisteringURingRestriction(e) => e.into(),
153        }
154    }
155}
156
157impl From<Error> for AsyncError {
158    fn from(e: Error) -> AsyncError {
159        AsyncError::SysVariants(e.into())
160    }
161}
162
163static IS_URING_STABLE: LazyLock<bool> = LazyLock::new(|| {
164    let mut utsname = MaybeUninit::zeroed();
165
166    // SAFETY:
167    // Safe because this will only modify `utsname` and we check the return value.
168    let res = unsafe { libc::uname(utsname.as_mut_ptr()) };
169    if res < 0 {
170        return false;
171    }
172
173    // SAFETY:
174    // Safe because the kernel has initialized `utsname`.
175    let utsname = unsafe { utsname.assume_init() };
176
177    // SAFETY:
178    // Safe because the pointer is valid and the kernel guarantees that this is a valid C string.
179    let release = unsafe { CStr::from_ptr(utsname.release.as_ptr()) };
180
181    let mut components = match release.to_str().map(|r| r.split('.').map(str::parse)) {
182        Ok(c) => c,
183        Err(_) => return false,
184    };
185
186    // Kernels older than 5.10 either didn't support io_uring or had bugs in the implementation.
187    match (components.next(), components.next()) {
188        (Some(Ok(major)), Some(Ok(minor))) if (major, minor) >= (5, 10) => {
189            // The kernel version is new enough so check if we can actually make a uring context.
190            URingContext::new(8, None).is_ok()
191        }
192        _ => false,
193    }
194});
195
196// Checks if the uring executor is stable.
197// Caches the result so that the check is only run once.
198// Useful for falling back to the FD executor on pre-uring kernels.
199pub fn is_uring_stable() -> bool {
200    *IS_URING_STABLE
201}
202
203// Checks the uring availability by checking if the uring creation succeeds.
204// If uring creation succeeds, it returns `Ok(())`. It returns an `URingContextError` otherwise.
205// It fails if the kernel does not support io_uring, but note that the cause is not limited to it.
206pub(crate) fn check_uring_availability() -> Result<()> {
207    URingContext::new(8, None)
208        .map(drop)
209        .map_err(Error::URingContextError)
210}
211
212pub struct RegisteredSource {
213    tag: usize,
214    ex: Weak<RawExecutor<UringReactor>>,
215}
216
217impl RegisteredSource {
218    pub fn start_read_to_mem(
219        &self,
220        file_offset: Option<u64>,
221        mem: Arc<dyn BackingMemory + Send + Sync>,
222        addrs: impl IntoIterator<Item = MemRegion>,
223    ) -> Result<PendingOperation> {
224        let ex = self.ex.upgrade().ok_or(Error::ExecutorGone)?;
225        let token = ex
226            .reactor
227            .submit_read_to_vectored(self, mem, file_offset, addrs)?;
228
229        Ok(PendingOperation {
230            waker_token: Some(token),
231            ex: self.ex.clone(),
232            submitted: false,
233        })
234    }
235
236    pub fn start_write_from_mem(
237        &self,
238        file_offset: Option<u64>,
239        mem: Arc<dyn BackingMemory + Send + Sync>,
240        addrs: impl IntoIterator<Item = MemRegion>,
241    ) -> Result<PendingOperation> {
242        let ex = self.ex.upgrade().ok_or(Error::ExecutorGone)?;
243        let token = ex
244            .reactor
245            .submit_write_from_vectored(self, mem, file_offset, addrs)?;
246
247        Ok(PendingOperation {
248            waker_token: Some(token),
249            ex: self.ex.clone(),
250            submitted: false,
251        })
252    }
253
254    pub fn start_fallocate(&self, offset: u64, len: u64, mode: u32) -> Result<PendingOperation> {
255        let ex = self.ex.upgrade().ok_or(Error::ExecutorGone)?;
256        let token = ex.reactor.submit_fallocate(self, offset, len, mode)?;
257
258        Ok(PendingOperation {
259            waker_token: Some(token),
260            ex: self.ex.clone(),
261            submitted: false,
262        })
263    }
264
265    pub fn start_fsync(&self) -> Result<PendingOperation> {
266        let ex = self.ex.upgrade().ok_or(Error::ExecutorGone)?;
267        let token = ex.reactor.submit_fsync(self)?;
268
269        Ok(PendingOperation {
270            waker_token: Some(token),
271            ex: self.ex.clone(),
272            submitted: false,
273        })
274    }
275
276    pub fn poll_fd_readable(&self) -> Result<PendingOperation> {
277        let events = EventType::Read;
278
279        let ex = self.ex.upgrade().ok_or(Error::ExecutorGone)?;
280        let token = ex.reactor.submit_poll(self, events)?;
281
282        Ok(PendingOperation {
283            waker_token: Some(token),
284            ex: self.ex.clone(),
285            submitted: false,
286        })
287    }
288}
289
290impl Drop for RegisteredSource {
291    fn drop(&mut self) {
292        if let Some(ex) = self.ex.upgrade() {
293            ex.reactor.deregister_source(self);
294        }
295    }
296}
297
298// Number of entries in the ring.
299const NUM_ENTRIES: usize = 256;
300
301// An operation that has been submitted to the uring and is potentially being waited on.
302struct OpData {
303    _file: Arc<File>,
304    _mem: Option<Arc<dyn BackingMemory + Send + Sync>>,
305    waker: Option<Waker>,
306    canceled: bool,
307}
308
309// The current status of an operation that's been submitted to the uring.
310enum OpStatus {
311    Nop,
312    Pending(OpData),
313    Completed(Option<::std::io::Result<u32>>),
314}
315
316struct Ring {
317    ops: Slab<OpStatus>,
318    registered_sources: Slab<Arc<File>>,
319}
320
321/// `Reactor` that manages async IO work using io_uring.
322pub struct UringReactor {
323    // The URingContext needs to be first so that it is dropped first, closing the uring fd, and
324    // releasing the resources borrowed by the kernel before we free them.
325    ctx: URingContext,
326    ring: Mutex<Ring>,
327    thread_id: Mutex<Option<ThreadId>>,
328}
329
330impl UringReactor {
331    fn new() -> Result<UringReactor> {
332        // Allow operations only that the UringReactor really submits to enhance the security.
333        let mut restrictions = URingAllowlist::new();
334        let ops = [
335            URingOperation::Writev,
336            URingOperation::Readv,
337            URingOperation::Write,
338            URingOperation::Read,
339            URingOperation::Nop,
340            URingOperation::Fsync,
341            URingOperation::Fallocate,
342            URingOperation::PollAdd,
343            URingOperation::PollRemove,
344            URingOperation::AsyncCancel,
345        ];
346        for op in ops {
347            restrictions.allow_submit_operation(op);
348        }
349
350        let ctx =
351            URingContext::new(NUM_ENTRIES, Some(&restrictions)).map_err(Error::CreatingContext)?;
352
353        Ok(UringReactor {
354            ctx,
355            ring: Mutex::new(Ring {
356                ops: Slab::with_capacity(NUM_ENTRIES),
357                registered_sources: Slab::with_capacity(NUM_ENTRIES),
358            }),
359            thread_id: Mutex::new(None),
360        })
361    }
362
363    fn runs_tasks_on_current_thread(&self) -> bool {
364        let executor_thread = self.thread_id.lock();
365        executor_thread
366            .map(|id| id == thread::current().id())
367            .unwrap_or(false)
368    }
369
370    fn get_result(&self, token: &WakerToken, cx: &mut Context) -> Option<io::Result<u32>> {
371        let mut ring = self.ring.lock();
372
373        let op = ring
374            .ops
375            .get_mut(token.0)
376            .expect("`get_result` called on unknown operation");
377        match op {
378            OpStatus::Nop => panic!("`get_result` called on nop"),
379            OpStatus::Pending(data) => {
380                if data.canceled {
381                    panic!("`get_result` called on canceled operation");
382                }
383                data.waker = Some(cx.waker().clone());
384                None
385            }
386            OpStatus::Completed(res) => {
387                let out = res.take();
388                ring.ops.remove(token.0);
389                Some(out.expect("Missing result in completed operation"))
390            }
391        }
392    }
393
394    // Remove the waker for the given token if it hasn't fired yet.
395    fn cancel_operation(&self, token: WakerToken) {
396        let mut ring = self.ring.lock();
397        let submit_cancel = if let Some(op) = ring.ops.get_mut(token.0) {
398            match op {
399                OpStatus::Nop => panic!("`cancel_operation` called on nop"),
400                OpStatus::Pending(data) => {
401                    if data.canceled {
402                        panic!("uring operation canceled more than once");
403                    }
404
405                    if let Some(waker) = data.waker.take() {
406                        waker.wake();
407                    }
408                    // Clear the waker as it is no longer needed.
409                    data.waker = None;
410                    data.canceled = true;
411
412                    // Keep the rest of the op data as the uring might still be accessing either
413                    // the source of the backing memory so it needs to live until the kernel
414                    // completes the operation.
415                    true
416                }
417                OpStatus::Completed(_) => {
418                    ring.ops.remove(token.0);
419                    false
420                }
421            }
422        } else {
423            false
424        };
425        std::mem::drop(ring);
426        if submit_cancel {
427            let _best_effort = self.submit_cancel_async(token.0);
428        }
429    }
430
431    pub(crate) fn register_source<F: AsRawDescriptor>(
432        &self,
433        raw: &Arc<RawExecutor<UringReactor>>,
434        fd: &F,
435    ) -> Result<RegisteredSource> {
436        // SAFETY:
437        // Safe because duplicating an FD doesn't affect memory safety, and the dup'd FD
438        // will only be added to the poll loop.
439        let duped_fd = unsafe { File::from_raw_fd(dup_fd(fd.as_raw_descriptor())?) };
440
441        Ok(RegisteredSource {
442            tag: self
443                .ring
444                .lock()
445                .registered_sources
446                .insert(Arc::new(duped_fd)),
447            ex: Arc::downgrade(raw),
448        })
449    }
450
451    fn deregister_source(&self, source: &RegisteredSource) {
452        // There isn't any need to pull pending ops out, the all have Arc's to the file and mem they
453        // need.let them complete. deregister with pending ops is not a common path no need to
454        // optimize that case yet.
455        self.ring.lock().registered_sources.remove(source.tag);
456    }
457
458    // Calls `add_op` with `source` and a freshly allocated token. Manages token allocation and
459    // preserving an Arc to the BackingMemory. The ring lock is held across `add_op` ensuring
460    // the operation is registered before its completion can be reaped.
461    fn submit_op<F>(
462        &self,
463        source: &RegisteredSource,
464        mem: Option<Arc<dyn BackingMemory + Send + Sync>>,
465        add_op: F,
466    ) -> Result<WakerToken>
467    where
468        F: FnOnce(RawDescriptor, u64) -> std::result::Result<(), io_uring::Error>,
469    {
470        let mut ring = self.ring.lock();
471        let src = ring
472            .registered_sources
473            .get(source.tag)
474            .ok_or(Error::InvalidSource)?
475            .clone();
476
477        let entry = ring.ops.vacant_entry();
478        let next_op_token = entry.key();
479
480        add_op(src.as_raw_descriptor(), usize_to_u64(next_op_token))
481            .map_err(Error::SubmittingOp)?;
482
483        entry.insert(OpStatus::Pending(OpData {
484            _file: src,
485            _mem: mem,
486            waker: None,
487            canceled: false,
488        }));
489
490        Ok(WakerToken(next_op_token))
491    }
492
493    fn submit_poll(
494        &self,
495        source: &RegisteredSource,
496        events: base::EventType,
497    ) -> Result<WakerToken> {
498        self.submit_op(source, None, |fd, token| {
499            self.ctx.add_poll_fd(fd, events, token)
500        })
501    }
502
503    fn submit_fallocate(
504        &self,
505        source: &RegisteredSource,
506        offset: u64,
507        len: u64,
508        mode: u32,
509    ) -> Result<WakerToken> {
510        self.submit_op(source, None, |fd, token| {
511            self.ctx.add_fallocate(fd, offset, len, mode, token)
512        })
513    }
514
515    fn submit_cancel_async(&self, token: usize) -> Result<WakerToken> {
516        let mut ring = self.ring.lock();
517        let entry = ring.ops.vacant_entry();
518        let next_op_token = entry.key();
519        self.ctx
520            .async_cancel(usize_to_u64(token), usize_to_u64(next_op_token))
521            .map_err(Error::SubmittingOp)?;
522
523        entry.insert(OpStatus::Nop);
524
525        Ok(WakerToken(next_op_token))
526    }
527
528    fn submit_fsync(&self, source: &RegisteredSource) -> Result<WakerToken> {
529        self.submit_op(source, None, |fd, token| self.ctx.add_fsync(fd, token))
530    }
531
532    fn submit_read_to_vectored(
533        &self,
534        source: &RegisteredSource,
535        mem: Arc<dyn BackingMemory + Send + Sync>,
536        offset: Option<u64>,
537        addrs: impl IntoIterator<Item = MemRegion>,
538    ) -> Result<WakerToken> {
539        let mut addrs = addrs.into_iter().peekable();
540        let first = addrs.next();
541
542        // A single region can be described by the queue entry itself, so `IORING_OP_READ` doesn't
543        // need an iovec array.
544        if let (Some(region), None) = (first, addrs.peek()) {
545            let (ptr, size) = {
546                let vslice = mem
547                    .get_volatile_slice(region)
548                    .map_err(|_| Error::InvalidOffset)?;
549                (vslice.as_mut_ptr(), vslice.size())
550            };
551            // The SQE holds the length in a 32-bit field; anything larger needs readv.
552            if let Ok(len) = u32::try_from(size) {
553                return self.submit_op(source, Some(mem), |fd, token| {
554                    // SAFETY:
555                    // Safe because the address is within the Memory that an Arc is kept for the
556                    // duration to ensure the memory is valid while the kernel accesses it.
557                    // Tested by `dont_drop_backing_mem_read` unit test.
558                    unsafe { self.ctx.add_read(ptr, len, fd, offset, token) }
559                });
560            }
561        }
562
563        let iovecs = first
564            .into_iter()
565            .chain(addrs)
566            .map(|mem_range| {
567                let vslice = mem
568                    .get_volatile_slice(mem_range)
569                    .map_err(|_| Error::InvalidOffset)?;
570                // SAFETY:
571                // Safe because we guarantee that the memory pointed to by `iovecs` lives until the
572                // transaction is complete and the completion has been returned from `wait()`.
573                Ok(unsafe { IoBufMut::from_raw_parts(vslice.as_mut_ptr(), vslice.size()) })
574            })
575            .collect::<Result<Vec<_>>>()?;
576        let iovecs = Pin::from(iovecs.into_boxed_slice());
577
578        self.submit_op(source, Some(mem), |fd, token| {
579            // SAFETY:
580            // Safe because all the addresses are within the Memory that an Arc is kept for the
581            // duration to ensure the memory is valid while the kernel accesses it.
582            // Tested by `dont_drop_backing_mem_read` unit test.
583            unsafe { self.ctx.add_readv(iovecs, fd, offset, token) }
584        })
585    }
586
587    fn submit_write_from_vectored(
588        &self,
589        source: &RegisteredSource,
590        mem: Arc<dyn BackingMemory + Send + Sync>,
591        offset: Option<u64>,
592        addrs: impl IntoIterator<Item = MemRegion>,
593    ) -> Result<WakerToken> {
594        let mut addrs = addrs.into_iter().peekable();
595        let first = addrs.next();
596
597        // A single region can be described by the queue entry itself, so `IORING_OP_WRITE` doesn't
598        // need an iovec array.
599        if let (Some(region), None) = (first, addrs.peek()) {
600            let (ptr, size) = {
601                let vslice = mem
602                    .get_volatile_slice(region)
603                    .map_err(|_| Error::InvalidOffset)?;
604                (vslice.as_ptr(), vslice.size())
605            };
606            // The SQE holds the length in a 32-bit field; anything larger needs writev.
607            if let Ok(len) = u32::try_from(size) {
608                return self.submit_op(source, Some(mem), |fd, token| {
609                    // SAFETY:
610                    // Safe because the address is within the Memory that an Arc is kept for the
611                    // duration to ensure the memory is valid while the kernel accesses it.
612                    // Tested by `dont_drop_backing_mem_write` unit test.
613                    unsafe { self.ctx.add_write(ptr, len, fd, offset, token) }
614                });
615            }
616        }
617
618        let iovecs = first
619            .into_iter()
620            .chain(addrs)
621            .map(|mem_range| {
622                let vslice = mem
623                    .get_volatile_slice(mem_range)
624                    .map_err(|_| Error::InvalidOffset)?;
625                // SAFETY:
626                // Safe because we guarantee that the memory pointed to by `iovecs` lives until the
627                // transaction is complete and the completion has been returned from `wait()`.
628                Ok(unsafe { IoBufMut::from_raw_parts(vslice.as_mut_ptr(), vslice.size()) })
629            })
630            .collect::<Result<Vec<_>>>()?;
631        let iovecs = Pin::from(iovecs.into_boxed_slice());
632
633        self.submit_op(source, Some(mem), |fd, token| {
634            // SAFETY:
635            // Safe because all the addresses are within the Memory that an Arc is kept for the
636            // duration to ensure the memory is valid while the kernel accesses it.
637            // Tested by `dont_drop_backing_mem_write` unit test.
638            unsafe { self.ctx.add_writev(iovecs, fd, offset, token) }
639        })
640    }
641}
642
643impl Reactor for UringReactor {
644    fn new() -> std::io::Result<Self> {
645        Ok(UringReactor::new()?)
646    }
647
648    fn wake(&self) {
649        let mut ring = self.ring.lock();
650        let entry = ring.ops.vacant_entry();
651        let next_op_token = entry.key();
652        if let Err(e) = self.ctx.add_nop(usize_to_u64(next_op_token)) {
653            warn!("Failed to add NOP for waking up executor: {}", e);
654        }
655        entry.insert(OpStatus::Nop);
656        mem::drop(ring);
657
658        match self.ctx.submit() {
659            Ok(()) => {}
660            // If the kernel's submit ring is full then we know we won't block when calling
661            // io_uring_enter, which is all we really care about.
662            Err(io_uring::Error::RingEnter(libc::EBUSY)) => {}
663            Err(e) => warn!("Failed to submit NOP for waking up executor: {}", e),
664        }
665    }
666
667    fn on_executor_drop<'a>(&'a self) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
668        // At this point, there are no strong references to the executor (see `on_executor_drop`
669        // docs). That means all the `RegisteredSource::ex` will fail to upgrade and so no more IO
670        // work can be submitted.
671
672        // Submit cancellations for all operations
673        #[allow(clippy::needless_collect)]
674        let ops: Vec<_> = self
675            .ring
676            .lock()
677            .ops
678            .iter_mut()
679            .filter_map(|op| match op.1 {
680                OpStatus::Pending(data) if !data.canceled => Some(op.0),
681                _ => None,
682            })
683            .collect();
684        for token in ops {
685            self.cancel_operation(WakerToken(token));
686        }
687
688        // Since the UringReactor is wrapped in an Arc it may end up being dropped from a different
689        // thread than the one that called `run` or `run_until`. Since we know there are no other
690        // references, just clear the thread id so that we don't panic.
691        *self.thread_id.lock() = None;
692
693        // Make sure all pending uring operations are completed as kernel may try to write to
694        // memory that we may drop.
695        //
696        // This future doesn't use the waker, it assumes the future will always be polled after
697        // processing other woken futures.
698        // TODO: Find a more robust solution.
699        Box::pin(futures::future::poll_fn(|_cx| {
700            if self.ring.lock().ops.is_empty() {
701                Poll::Ready(())
702            } else {
703                Poll::Pending
704            }
705        }))
706    }
707
708    fn on_thread_start(&self) {
709        let current_thread = thread::current().id();
710        let mut thread_id = self.thread_id.lock();
711        assert_eq!(
712            *thread_id.get_or_insert(current_thread),
713            current_thread,
714            "`UringReactor::wait_for_work` cannot be called from more than one thread"
715        );
716    }
717
718    fn wait_for_work(&self, set_processing: impl Fn()) -> std::io::Result<()> {
719        trace!(
720            "Waiting on events, {} pending ops",
721            self.ring.lock().ops.len()
722        );
723        let events = self.ctx.wait().map_err(Error::URingEnter)?;
724
725        // Set the state back to PROCESSING to prevent any tasks woken up by the loop below from
726        // writing to the eventfd.
727        set_processing();
728
729        let mut ring = self.ring.lock();
730        for (raw_token, result) in events {
731            // While the `expect()` might fail on arbitrary `u64`s, the `raw_token` was
732            // something that we originally gave to the kernel and that was created from a
733            // `usize` so we should always be able to convert it back into a `usize`.
734            let token = raw_token
735                .try_into()
736                .expect("`u64` doesn't fit inside a `usize`");
737
738            let op = ring
739                .ops
740                .get_mut(token)
741                .expect("Received completion token for unexpected operation");
742            match mem::replace(op, OpStatus::Completed(Some(result))) {
743                // No one is waiting on a Nop.
744                OpStatus::Nop => mem::drop(ring.ops.remove(token)),
745                OpStatus::Pending(data) => {
746                    if data.canceled {
747                        // No one is waiting for this operation and the uring is done with
748                        // it so it's safe to remove.
749                        ring.ops.remove(token);
750                    }
751                    if let Some(waker) = data.waker {
752                        waker.wake();
753                    }
754                }
755                OpStatus::Completed(_) => panic!("uring operation completed more than once"),
756            }
757        }
758
759        Ok(())
760    }
761
762    fn new_source<F: AsRawDescriptor>(
763        &self,
764        ex: &Arc<RawExecutor<Self>>,
765        f: F,
766    ) -> AsyncResult<IoSource<F>> {
767        Ok(IoSource::Uring(super::UringSource::new(f, ex)?))
768    }
769
770    fn wrap_task_handle<R>(task: RawTaskHandle<UringReactor, R>) -> TaskHandle<R> {
771        TaskHandle::Uring(task)
772    }
773}
774
775impl AsRawDescriptor for UringReactor {
776    fn as_raw_descriptor(&self) -> RawDescriptor {
777        self.ctx.as_raw_descriptor()
778    }
779}
780
781impl WeakWake for UringReactor {
782    fn wake_by_ref(weak_self: &Weak<Self>) {
783        if let Some(arc_self) = weak_self.upgrade() {
784            Reactor::wake(&*arc_self);
785        }
786    }
787}
788
789impl Drop for UringReactor {
790    fn drop(&mut self) {
791        // The ring should have been drained when the executor's Drop ran.
792        assert!(self.ring.lock().ops.is_empty());
793    }
794}
795
796// SAFETY:
797// Used to dup the FDs passed to the executor so there is a guarantee they aren't closed while
798// waiting in TLS to be added to the main polling context.
799unsafe fn dup_fd(fd: RawFd) -> Result<RawFd> {
800    let ret = libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, 0);
801    if ret < 0 {
802        Err(Error::DuplicatingFd(base::Error::last()))
803    } else {
804        Ok(ret)
805    }
806}
807
808// Converts a `usize` into a `u64` and panics if the conversion fails.
809#[inline]
810fn usize_to_u64(val: usize) -> u64 {
811    val.try_into().expect("`usize` doesn't fit inside a `u64`")
812}
813
814pub struct PendingOperation {
815    waker_token: Option<WakerToken>,
816    ex: Weak<RawExecutor<UringReactor>>,
817    submitted: bool,
818}
819
820impl Future for PendingOperation {
821    type Output = Result<u32>;
822
823    fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
824        let token = self
825            .waker_token
826            .as_ref()
827            .expect("PendingOperation polled after returning Poll::Ready");
828        if let Some(ex) = self.ex.upgrade() {
829            if let Some(result) = ex.reactor.get_result(token, cx) {
830                self.waker_token = None;
831                Poll::Ready(result.map_err(Error::Io))
832            } else {
833                // If we haven't submitted the operation yet, and the executor runs on a different
834                // thread then submit it now. Otherwise the executor will submit it automatically
835                // the next time it calls UringContext::wait.
836                if !self.submitted && !ex.reactor.runs_tasks_on_current_thread() {
837                    match ex.reactor.ctx.submit() {
838                        Ok(()) => self.submitted = true,
839                        // If the kernel ring is full then wait until some ops are removed from the
840                        // completion queue. This op should get submitted the next time the executor
841                        // calls UringContext::wait.
842                        Err(io_uring::Error::RingEnter(libc::EBUSY)) => {}
843                        Err(e) => return Poll::Ready(Err(Error::URingEnter(e))),
844                    }
845                }
846                Poll::Pending
847            }
848        } else {
849            Poll::Ready(Err(Error::ExecutorGone))
850        }
851    }
852}
853
854impl Drop for PendingOperation {
855    fn drop(&mut self) {
856        if let Some(waker_token) = self.waker_token.take() {
857            if let Some(ex) = self.ex.upgrade() {
858                ex.reactor.cancel_operation(waker_token);
859            }
860        }
861    }
862}
863
864#[cfg(test)]
865mod tests {
866    use std::future::Future;
867    use std::io::Read;
868    use std::io::Write;
869    use std::mem;
870    use std::pin::Pin;
871    use std::rc::Rc;
872    use std::task::Context;
873    use std::task::Poll;
874
875    use futures::executor::block_on;
876
877    use super::*;
878    use crate::mem::BackingMemory;
879    use crate::mem::MemRegion;
880    use crate::mem::VecIoWrapper;
881    use crate::BlockingPool;
882    use crate::ExecutorTrait;
883
884    // A future that returns ready when the uring queue is empty.
885    struct UringQueueEmpty<'a> {
886        ex: &'a Arc<RawExecutor<UringReactor>>,
887    }
888
889    impl Future for UringQueueEmpty<'_> {
890        type Output = ();
891
892        fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
893            if self.ex.reactor.ring.lock().ops.is_empty() {
894                Poll::Ready(())
895            } else {
896                Poll::Pending
897            }
898        }
899    }
900
901    #[test]
902    fn dont_drop_backing_mem_read() {
903        if !is_uring_stable() {
904            return;
905        }
906
907        // Create a backing memory wrapped in an Arc and check that the drop isn't called while the
908        // op is pending.
909        let bm =
910            Arc::new(VecIoWrapper::from(vec![0u8; 4096])) as Arc<dyn BackingMemory + Send + Sync>;
911
912        // Use pipes to create a future that will block forever.
913        let (rx, mut tx) = base::pipe().unwrap();
914
915        // Set up the TLS for the uring_executor by creating one.
916        let ex = RawExecutor::<UringReactor>::new().unwrap();
917
918        // Register the receive side of the pipe with the executor.
919        let registered_source = ex
920            .reactor
921            .register_source(&ex, &rx)
922            .expect("register source failed");
923
924        // Submit the op to the kernel. Next, test that the source keeps its Arc open for the
925        // duration of the op.
926        let pending_op = registered_source
927            .start_read_to_mem(None, Arc::clone(&bm), [MemRegion { offset: 0, len: 8 }])
928            .expect("failed to start read to mem");
929
930        // Here the Arc count must be two, one for `bm` and one to signify that the kernel has a
931        // reference while the op is active.
932        assert_eq!(Arc::strong_count(&bm), 2);
933
934        // Dropping the operation shouldn't reduce the Arc count, as the kernel could still be using
935        // it.
936        drop(pending_op);
937        assert_eq!(Arc::strong_count(&bm), 2);
938
939        // Finishing the operation should put the Arc count back to 1.
940        // write to the pipe to wake the read pipe and then wait for the uring result in the
941        // executor.
942        tx.write_all(&[0u8; 8]).expect("write failed");
943        ex.run_until(UringQueueEmpty { ex: &ex })
944            .expect("Failed to wait for read pipe ready");
945        assert_eq!(Arc::strong_count(&bm), 1);
946    }
947
948    #[test]
949    fn dont_drop_backing_mem_write() {
950        if !is_uring_stable() {
951            return;
952        }
953
954        // Create a backing memory wrapped in an Arc and check that the drop isn't called while the
955        // op is pending.
956        let bm =
957            Arc::new(VecIoWrapper::from(vec![0u8; 4096])) as Arc<dyn BackingMemory + Send + Sync>;
958
959        // Use pipes to create a future that will block forever.
960        let (mut rx, tx) = base::new_pipe_full().expect("Pipe failed");
961
962        // Set up the TLS for the uring_executor by creating one.
963        let ex = RawExecutor::<UringReactor>::new().unwrap();
964
965        // Register the receive side of the pipe with the executor.
966        let registered_source = ex
967            .reactor
968            .register_source(&ex, &tx)
969            .expect("register source failed");
970
971        // Submit the op to the kernel. Next, test that the source keeps its Arc open for the
972        // duration of the op.
973        let pending_op = registered_source
974            .start_write_from_mem(None, Arc::clone(&bm), [MemRegion { offset: 0, len: 8 }])
975            .expect("failed to start write to mem");
976
977        // Here the Arc count must be two, one for `bm` and one to signify that the kernel has a
978        // reference while the op is active.
979        assert_eq!(Arc::strong_count(&bm), 2);
980
981        // Dropping the operation shouldn't reduce the Arc count, as the kernel could still be using
982        // it.
983        drop(pending_op);
984        assert_eq!(Arc::strong_count(&bm), 2);
985
986        // Finishing the operation should put the Arc count back to 1.
987        // write to the pipe to wake the read pipe and then wait for the uring result in the
988        // executor.
989        let mut buf = vec![0u8; base::round_up_to_page_size(1)];
990        rx.read_exact(&mut buf).expect("read to empty failed");
991        ex.run_until(UringQueueEmpty { ex: &ex })
992            .expect("Failed to wait for write pipe ready");
993        assert_eq!(Arc::strong_count(&bm), 1);
994    }
995
996    #[test]
997    fn canceled_before_completion() {
998        if !is_uring_stable() {
999            return;
1000        }
1001
1002        async fn cancel_io(op: PendingOperation) {
1003            mem::drop(op);
1004        }
1005
1006        async fn check_result(op: PendingOperation, expected: u32) {
1007            let actual = op.await.expect("operation failed to complete");
1008            assert_eq!(expected, actual);
1009        }
1010
1011        let bm =
1012            Arc::new(VecIoWrapper::from(vec![0u8; 16])) as Arc<dyn BackingMemory + Send + Sync>;
1013
1014        let (rx, tx) = base::pipe().expect("Pipe failed");
1015
1016        let ex = RawExecutor::<UringReactor>::new().unwrap();
1017
1018        let rx_source = ex
1019            .reactor
1020            .register_source(&ex, &rx)
1021            .expect("register source failed");
1022        let tx_source = ex
1023            .reactor
1024            .register_source(&ex, &tx)
1025            .expect("register source failed");
1026
1027        let read_task = rx_source
1028            .start_read_to_mem(None, Arc::clone(&bm), [MemRegion { offset: 0, len: 8 }])
1029            .expect("failed to start read to mem");
1030
1031        ex.spawn_local(cancel_io(read_task)).detach();
1032
1033        // Write to the pipe so that the kernel operation will complete.
1034        let buf =
1035            Arc::new(VecIoWrapper::from(vec![0xc2u8; 16])) as Arc<dyn BackingMemory + Send + Sync>;
1036        let write_task = tx_source
1037            .start_write_from_mem(None, Arc::clone(&buf), [MemRegion { offset: 0, len: 8 }])
1038            .expect("failed to start write from mem");
1039
1040        ex.run_until(check_result(write_task, 8))
1041            .expect("Failed to run executor");
1042    }
1043
1044    // We will drain all ops on drop and its not guaranteed that operation won't finish
1045    #[ignore]
1046    #[test]
1047    fn drop_before_completion() {
1048        if !is_uring_stable() {
1049            return;
1050        }
1051
1052        const VALUE: u64 = 0xef6c_a8df_b842_eb9c;
1053
1054        async fn check_op(op: PendingOperation) {
1055            let err = op.await.expect_err("Op completed successfully");
1056            match err {
1057                Error::ExecutorGone => {}
1058                e => panic!("Unexpected error from op: {e}"),
1059            }
1060        }
1061
1062        let (mut rx, mut tx) = base::pipe().expect("Pipe failed");
1063
1064        let ex = RawExecutor::<UringReactor>::new().unwrap();
1065
1066        let tx_source = ex
1067            .reactor
1068            .register_source(&ex, &tx)
1069            .expect("Failed to register source");
1070        let bm = Arc::new(VecIoWrapper::from(VALUE.to_ne_bytes().to_vec()));
1071        let op = tx_source
1072            .start_write_from_mem(
1073                None,
1074                bm,
1075                [MemRegion {
1076                    offset: 0,
1077                    len: mem::size_of::<u64>(),
1078                }],
1079            )
1080            .expect("Failed to start write from mem");
1081
1082        ex.spawn_local(check_op(op)).detach();
1083
1084        // Now drop the executor. It shouldn't run the write operation.
1085        mem::drop(ex);
1086
1087        // Make sure the executor did not complete the uring operation.
1088        let new_val = [0x2e; 8];
1089        tx.write_all(&new_val).unwrap();
1090
1091        let mut buf = 0u64.to_ne_bytes();
1092        rx.read_exact(&mut buf[..])
1093            .expect("Failed to read from pipe");
1094
1095        assert_eq!(buf, new_val);
1096    }
1097
1098    // Dropping a task that owns a BlockingPool shouldn't leak the pool.
1099    #[test]
1100    fn drop_detached_blocking_pool() {
1101        if !is_uring_stable() {
1102            return;
1103        }
1104
1105        struct Cleanup(BlockingPool);
1106
1107        impl Drop for Cleanup {
1108            fn drop(&mut self) {
1109                // Make sure we shutdown cleanly (BlockingPool::drop just prints a warning).
1110                self.0
1111                    .shutdown(Some(
1112                        std::time::Instant::now() + std::time::Duration::from_secs(1),
1113                    ))
1114                    .unwrap();
1115            }
1116        }
1117
1118        let rc = Rc::new(std::cell::Cell::new(0));
1119        {
1120            let ex = RawExecutor::<UringReactor>::new().unwrap();
1121            let rc_clone = rc.clone();
1122            ex.spawn_local(async move {
1123                rc_clone.set(1);
1124                let pool = Cleanup(BlockingPool::new(1, std::time::Duration::new(60, 0)));
1125                let (send, recv) = std::sync::mpsc::sync_channel::<()>(0);
1126                // Spawn a blocking task.
1127                let blocking_task = pool.0.spawn(move || {
1128                    // Rendezvous.
1129                    assert_eq!(recv.recv(), Ok(()));
1130                    // Wait for drop.
1131                    assert_eq!(recv.recv(), Err(std::sync::mpsc::RecvError));
1132                });
1133                // Make sure it has actually started (using a "rendezvous channel" send).
1134                //
1135                // Without this step, we'll have a race where we can shutdown the blocking pool
1136                // before the worker thread pops off the task.
1137                send.send(()).unwrap();
1138                // Wait for it to finish
1139                blocking_task.await;
1140                rc_clone.set(2);
1141            })
1142            .detach();
1143            ex.run_until(async {}).unwrap();
1144            // `ex` is dropped here. If everything is working as expected, it should drop all of
1145            // its tasks, including `send` and `pool` (in that order, which is important). `pool`'s
1146            // `Drop` impl will try to join all the worker threads, which should work because send
1147            // half of the channel closed.
1148        }
1149        assert_eq!(rc.get(), 1);
1150        Rc::try_unwrap(rc).expect("Rc had too many refs");
1151    }
1152
1153    #[test]
1154    fn drop_on_different_thread() {
1155        if !is_uring_stable() {
1156            return;
1157        }
1158
1159        let ex = RawExecutor::<UringReactor>::new().unwrap();
1160
1161        let ex2 = ex.clone();
1162        let t = thread::spawn(move || ex2.run_until(async {}));
1163
1164        t.join().unwrap().unwrap();
1165
1166        // Leave an uncompleted operation in the queue so that the drop impl will try to drive it to
1167        // completion.
1168        let (_rx, tx) = base::pipe().expect("Pipe failed");
1169        let tx = ex
1170            .reactor
1171            .register_source(&ex, &tx)
1172            .expect("Failed to register source");
1173        let bm = Arc::new(VecIoWrapper::from(0xf2e96u64.to_ne_bytes().to_vec()));
1174        let op = tx
1175            .start_write_from_mem(
1176                None,
1177                bm,
1178                [MemRegion {
1179                    offset: 0,
1180                    len: mem::size_of::<u64>(),
1181                }],
1182            )
1183            .expect("Failed to start write from mem");
1184
1185        mem::drop(ex);
1186
1187        match block_on(op).expect_err("Pending operation completed after executor was dropped") {
1188            Error::ExecutorGone => {}
1189            e => panic!("Unexpected error after dropping executor: {e}"),
1190        }
1191    }
1192}