171 lines · 6.1 KB · Rust
RawDownload
// SPDX-License-Identifier: GPL-2.0 //! Sleepable read-copy update (SRCU) support.//!//! C header: [`include/linux/srcu.h`](srctree/include/linux/srcu.h) use crate::{    bindings,    error::to_result,    prelude::*,    sync::LockClassKey,    types::{        NotThreadSafe,        Opaque, //    },}; use pin_init::pin_data; /// Creates an [`Srcu`] initialiser with the given name and a newly-created lock class.#[doc(hidden)]#[macro_export]macro_rules! new_srcu {    ($($name:literal)?) => {        $crate::sync::Srcu::new($crate::optional_name!($($name)?), $crate::static_lock_class!())    };}pub use new_srcu; /// Sleepable read-copy update primitive.////// SRCU readers may sleep while holding the read-side guard.////// The destructor waits for active readers and callbacks, so it may sleep./// If a read-side guard has been leaked, dropping an [`Srcu`] may never return.////// # Invariants////// This represents a valid `struct srcu_struct` initialized by the C SRCU API/// and it remains pinned and valid until the pinned destructor runs.#[repr(transparent)]#[pin_data(PinnedDrop)]pub struct Srcu {    #[pin]    inner: Opaque<bindings::srcu_struct>,} impl Srcu {    /// Creates a new SRCU instance.    #[inline]    pub fn new(name: &'static CStr, key: Pin<&'static LockClassKey>) -> impl PinInit<Self, Error> {        try_pin_init!(Self {            // INVARIANT: On success, the C initializer creates a valid `srcu_struct` and            // it remains pinned until `PinnedDrop` runs.            inner <- Opaque::try_ffi_init(|ptr: *mut bindings::srcu_struct| {                // SAFETY: `ptr` points to valid uninitialised memory for a `srcu_struct`.                to_result(unsafe {                    bindings::init_srcu_struct_with_key(ptr, name.as_char_ptr(), key.as_ptr())                })            }),        })    }     /// Enters an SRCU read-side critical section.    ///    /// Leaking the returned [`Guard`] leaves the SRCU read-side critical    /// section active and makes `drop` sleep forever.    #[inline]    pub fn read_lock(&self) -> Guard<'_> {        // SAFETY: By the type invariants, `self` contains a valid `struct srcu_struct`.        let idx = unsafe { bindings::srcu_read_lock(self.inner.get()) };         // INVARIANT: `idx` was returned by `srcu_read_lock()` for this `Srcu`.        Guard {            srcu: self,            idx,            _not_send: NotThreadSafe,        }    }     /// Waits until all pre-existing SRCU readers have completed.    #[inline]    pub fn synchronize(&self) {        // SAFETY: By the type invariants, `self` contains a valid `struct srcu_struct`.        unsafe { bindings::synchronize_srcu(self.inner.get()) };    }     /// Waits until all pre-existing SRCU readers have completed, expedited.    ///    /// This requests a lower-latency grace period than [`Srcu::synchronize`] typically    /// at the cost of higher system-wide overhead. Prefer [`Srcu::synchronize`] by default    /// and use this variant only when reducing reset or teardown latency is more important    /// than the extra cost.    #[inline]    pub fn synchronize_expedited(&self) {        // SAFETY: By the type invariants, `self` contains a valid `struct srcu_struct`.        unsafe { bindings::synchronize_srcu_expedited(self.inner.get()) };    }} #[pinned_drop]impl PinnedDrop for Srcu {    fn drop(self: Pin<&mut Self>) {        let ptr = self.inner.get();         if crate::warn_on!(            // SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`            // and `srcu_readers_active()` only checks the active reader count.            unsafe { bindings::srcu_readers_active(ptr) }        ) {            // `cleanup_srcu_struct()` may return early if there are still active readers.            // This should only happen if a guard was leaked with `mem::forget`, which is            // "WRONG" code and may cause a UAF because Rust will free the `srcu_struct`            // while it is still referenced from the C side (e.g. by `call_srcu()` callbacks).            //            // Another consequence of leaking guards is that `call_srcu()` callbacks will            // never run because the grace period can never complete due to permanently            // active readers (i.e. leaked guards).            //            // If this ever happens, that means the guard was leaked by mistake and the            // caller must fix the bug. Sleeping here is intentional and less harmful            // than risking a UAF.            //            // SAFETY: By the type invariants, `self` contains a valid and pinned            // `struct srcu_struct`.            unsafe { bindings::synchronize_srcu(ptr) };        }         // Ensure all SRCU callbacks have been finished before freeing.        // SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`.        unsafe { bindings::srcu_barrier(ptr) };         // SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`.        unsafe { bindings::cleanup_srcu_struct(ptr) };    }} // SAFETY: `srcu_struct` may be shared and used across threads.unsafe impl Send for Srcu {}// SAFETY: `srcu_struct` may be shared and used concurrently.unsafe impl Sync for Srcu {} /// Guard for an active SRCU read-side critical section on a particular [`Srcu`].////// Leaking this guard with [`core::mem::forget`] leaves the SRCU read-side/// critical section active and makes dropping the associated [`Srcu`] sleep forever.////// # Invariants////// `idx` is the index returned by `srcu_read_lock()` for `srcu`.#[must_use = "if unused, the lock will be immediately unlocked"]pub struct Guard<'a> {    srcu: &'a Srcu,    idx: i32,    _not_send: NotThreadSafe,} impl Guard<'_> {    /// Explicitly releases the SRCU read-side critical section.    #[inline]    pub fn unlock(self) {}} impl Drop for Guard<'_> {    #[inline]    fn drop(&mut self) {        // SAFETY: `Guard` is only constructible through `Srcu::read_lock()`,        // which returns a valid index for the SRCU instance.        unsafe { bindings::srcu_read_unlock(self.srcu.inner.get(), self.idx) };    }}