Add RCU bindings, including an RAII guard for rcu_read_lock() &
rcu_read_unlock(), and an RcuCell to model qatomic_rcu_read within an
RCU critical section.

Suggested-by: Paolo Bonzini <[email protected]>
Suggested-by: Manos Pitsidianakis <[email protected]>
Signed-off-by: Zhao Liu <[email protected]>
---
Changes since v1:
 * Use RAII pattern to guard RCU locking.
 * New RcuCell to simplify RCU read in Rust.
---
 rust/hw/char/pl011/wrapper.h |   1 +
 rust/util/src/lib.rs         |   1 +
 rust/util/src/rcu.rs         | 137 +++++++++++++++++++++++++++++++++++
 3 files changed, 139 insertions(+)
 create mode 100644 rust/util/src/rcu.rs

diff --git a/rust/hw/char/pl011/wrapper.h b/rust/hw/char/pl011/wrapper.h
index 87a5a589c8ea..2f66683fcd10 100644
--- a/rust/hw/char/pl011/wrapper.h
+++ b/rust/hw/char/pl011/wrapper.h
@@ -49,3 +49,4 @@ typedef enum memory_order {
 
 #include "qemu/osdep.h"
 #include "hw/char/pl011.h"
+#include "qemu/rcu.h"
diff --git a/rust/util/src/lib.rs b/rust/util/src/lib.rs
index 436c67e139ed..0736c1883dbf 100644
--- a/rust/util/src/lib.rs
+++ b/rust/util/src/lib.rs
@@ -5,6 +5,7 @@
 pub mod error;
 pub mod log;
 pub mod module;
+pub mod rcu;
 
 // preserve one-item-per-"use" syntax, it is clearer
 // for prelude-like modules
diff --git a/rust/util/src/rcu.rs b/rust/util/src/rcu.rs
new file mode 100644
index 000000000000..e233f9698bfd
--- /dev/null
+++ b/rust/util/src/rcu.rs
@@ -0,0 +1,137 @@
+// Copyright (C) 2026 Intel Corporation.
+// Author(s): Zhao Liu <[email protected]>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//! Bindings for `rcu_read_lock` and `rcu_read_unlock`.
+//! More details about RCU in QEMU, please refer docs/devel/rcu.rst.
+
+use std::{
+    marker::PhantomData,
+    sync::atomic::{AtomicPtr, Ordering},
+};
+
+use crate::bindings;
+
+/// RAII guard for the RCU read side lock.  The lock is acquired on creation 
and
+/// released on drop.
+///
+/// This guard is not `Send` or `Sync`, so it cannot be moved to another thread
+/// or stored in a global variable.  This ensures the RCU read side lock to be
+/// held and unlocked in the same thread.  The guard is also not `Clone` or
+/// `Copy`, so it cannot be duplicated and this can avoid double-unlock, which
+/// would be a bug.
+///
+/// ```ignore
+/// let guard = RcuGuard::new();
+/// // do some RCU read-side work
+/// drop(guard); // releases the RCU read side lock
+/// ```
+pub struct RcuGuard {
+    _marker: PhantomData<*const ()>,
+}
+
+impl RcuGuard {
+    /// Acquires the RCU read side lock and returns a guard.
+    #[inline]
+    pub fn new() -> Self {
+        // SAFETY: An FFI call with no additional requirements.
+        unsafe {
+            bindings::rust_rcu_read_lock();
+        }
+        // INVARIANT: The RCU read side lock was just acquired above.
+        Self {
+            _marker: PhantomData,
+        }
+    }
+}
+
+impl Default for RcuGuard {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+impl Drop for RcuGuard {
+    #[inline]
+    fn drop(&mut self) {
+        // SAFETY: An FFI call with no additional requirements.
+        unsafe {
+            bindings::rust_rcu_read_unlock();
+        }
+    }
+}
+
+/// (Reader-side view of) an RCU-protected C pointer wrapper.
+///
+/// This wrapper expresses the RCU read semantics of the C side:
+///
+/// ```c
+/// rcu_read_lock();
+/// p = qatomic_rcu_read(&foo);
+/// /* do something with p. */
+/// rcu_read_unlock();
+/// ```
+///
+/// With this wrapper, the above code can be expressed as:
+///
+/// ```ignore
+/// // SAFETY: `foo_ptr` is a live RCU-protected pointer.
+/// let cell = unsafe { RcuCell::from_ptr(foo_ptr) };
+/// {
+///     let guard = RcuGuard::new();
+///     if let Some(p) = cell.get(&guard) {
+///         /* do something with p. */
+///     }
+/// } // `rcu_read_unlock()` runs here, when `guard` is dropped.
+/// ```
+///
+/// # Ordering vs. C's `qatomic_rcu_read`
+///
+/// C's `qatomic_rcu_read` compiles to `__ATOMIC_RELAXED` followed by
+/// `smp_read_barrier_depends()` (or `__ATOMIC_CONSUME` under TSAN).
+/// `smp_read_barrier_depends()` is a *compiler* barrier on all hosts QEMU
+/// supports (see [docs/devel/atomics.rst](../../docs/devel/atomics.rst)),
+/// providing dependency ordering.
+///
+/// Rust exposes neither `Ordering::Consume` nor a dependency-barrier
+/// primitive.  So there is no exact Rust equivalent of `qatomic_rcu_read`.
+/// Instead, use [`Ordering::Acquire`], the strictly stronger option:
+/// - `Acquire` is a correct upper bound: every ordering that C's
+///   `qatomic_rcu_read` guarantees is also guaranteed by `Acquire`, and it
+///   correctly pairs with the updater's `qatomic_rcu_set()` (a Release store).
+#[repr(transparent)]
+pub struct RcuCell<T> {
+    data: AtomicPtr<T>,
+}
+
+impl<T> RcuCell<T> {
+    /// Convert a C raw pointer to an [`RcuCell`] in place, without copying it.
+    ///
+    /// Note: copying the pointer first and then loading the copy would
+    /// synchronise with nothing: the `Ordering::Acquire` must be applied to
+    /// the original pointer to pair with `qatomic_rcu_set()`.
+    ///
+    /// # Safety
+    ///
+    /// `field` must point to a valid `*mut T` updated only with
+    /// `qatomic_rcu_set()`, and the `T` object pointed to by `*field` must
+    ///  remain valid during the RCU read-side critical section entered before
+    ///  calling `RcuCell<T>::get`.
+    pub unsafe fn from_ptr<'a>(field: *const *mut T) -> &'a Self {
+        // SAFETY: `RcuCell` is `repr(transparent)` over `AtomicPtr<T>`.
+        unsafe { &*(field.cast::<Self>()) }
+    }
+
+    /// An almost equivalent of `qatomic_rcu_read()`, but with
+    /// `Ordering::Acquire`.
+    #[inline]
+    fn raw_get(&self) -> *mut T {
+        self.data.load(Ordering::Acquire)
+    }
+
+    pub fn get<'g>(&self, _: &'g RcuGuard) -> Option<&'g T> {
+        // SAFETY: the reference is *shared*, which allows several readers
+        // to hold the RCU lock concurrently.
+        unsafe { self.raw_get().as_ref() }
+    }
+}
-- 
2.34.1


Reply via email to