qemu-rust.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Zhao Liu <zhao1.liu@intel.com>
To: "Paolo Bonzini" <pbonzini@redhat.com>,
	"Manos Pitsidianakis" <manos.pitsidianakis@linaro.org>,
	"Marc-André Lureau" <marcandre.lureau@redhat.com>
Cc: Igor Mammedov <imammedo@redhat.com>,
	qemu-devel@nongnu.org, qemu-rust@nongnu.org,
	Zhao Liu <zhao1.liu@intel.com>
Subject: [PATCH 04/22] rust/bql: Add BqlGuard to provide BQL context
Date: Thu, 13 Nov 2025 13:19:19 +0800	[thread overview]
Message-ID: <20251113051937.4017675-5-zhao1.liu@intel.com> (raw)
In-Reply-To: <20251113051937.4017675-1-zhao1.liu@intel.com>

There's some case requiring BQL context, for example, accessing
BqlCell/BqlRefCell or operation on IRQ in lockless IO.

Note though BqlCell/BqlRefCell ensures BQL won't be released during
reference exists, they don't create BQL context if BQL is not locked,
instead, they just assert and panic when BQL is not locked.

Therefore, it's necessary to provide a way to create BQL context by hand
at Rust side. BqlGuard is suitable for this need.

Signed-off-by: Zhao Liu <zhao1.liu@intel.com>
---
 include/qemu/main-loop.h |  22 ++++++++-
 rust/bql/src/lib.rs      | 101 ++++++++++++++++++++++++++++++++++++++-
 stubs/iothread-lock.c    |  11 +++++
 system/cpus.c            |  10 ++++
 4 files changed, 142 insertions(+), 2 deletions(-)

diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h
index 0d55c636b21a..7dd7c8211f02 100644
--- a/include/qemu/main-loop.h
+++ b/include/qemu/main-loop.h
@@ -250,7 +250,7 @@ AioContext *iohandler_get_aio_context(void);
 /**
  * rust_bql_mock_lock:
  *
- * Called from Rust doctests to make bql_lock() return true.
+ * Called from Rust doctests to make bql_locked() return true.
  * Do not touch.
  */
 void rust_bql_mock_lock(void);
@@ -368,6 +368,16 @@ bool qemu_in_main_thread(void);
 #define bql_lock() bql_lock_impl(__FILE__, __LINE__)
 void bql_lock_impl(const char *file, int line);
 
+/**
+ * @rust_bql_lock: A wrapper over bql_lock().
+ *
+ * This function is used to allow bindgen to generate bql_lock()
+ * binding.
+ *
+ * Do not call this function directly! Use bql_lock() instead.
+ */
+void rust_bql_lock(void);
+
 /**
  * bql_unlock: Unlock the Big QEMU Lock (BQL).
  *
@@ -383,6 +393,16 @@ void bql_lock_impl(const char *file, int line);
  */
 void bql_unlock(void);
 
+/**
+ * @rust_bql_unlock: A wrapper over bql_unlock().
+ *
+ * This function is used to allow bindgen to generate bql_unlock()
+ * binding.
+ *
+ * Do not call this function directly! Use bql_unlock() instead.
+ */
+void rust_bql_unlock(void);
+
 /**
  * BQL_LOCK_GUARD
  *
diff --git a/rust/bql/src/lib.rs b/rust/bql/src/lib.rs
index ef08221e9c1a..48cc76a7557c 100644
--- a/rust/bql/src/lib.rs
+++ b/rust/bql/src/lib.rs
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 
 mod bindings;
-use bindings::{bql_block_unlock, bql_locked, rust_bql_mock_lock};
+use bindings::{bql_block_unlock, bql_locked, rust_bql_lock, rust_bql_mock_lock, rust_bql_unlock};
 
 mod cell;
 pub use cell::*;
@@ -27,3 +27,102 @@ pub fn block_unlock(increase: bool) {
         bql_block_unlock(increase);
     }
 }
+
+// this function is private since user should get BQL context via BqlGuard.
+fn lock() {
+    // SAFETY: the function locks bql which lifetime is enough to cover
+    // the device's entire lifetime.
+    unsafe {
+        rust_bql_lock();
+    }
+}
+
+// this function is private since user should get BQL context via BqlGuard.
+fn unlock() {
+    // SAFETY: the function unlocks bql which lifetime is enough to
+    // cover the device's entire lifetime.
+    unsafe {
+        rust_bql_unlock();
+    }
+}
+
+/// An RAII guard to ensure a block of code runs within the BQL context.
+///
+/// It checks if the BQL is already locked at its creation:
+/// * If not, it locks the BQL and will release it when it is dropped.
+/// * If yes, it blocks BQL unlocking until its lifetime is end.
+#[must_use]
+pub struct BqlGuard {
+    locked: bool,
+}
+
+impl BqlGuard {
+    /// Creates a new `BqlGuard` to ensure BQL is locked during its
+    /// lifetime.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use bql::{BqlCell, BqlGuard};
+    ///
+    /// fn foo() {
+    ///     let _guard = BqlGuard::new(); // BQL is locked
+    ///
+    ///     let c = BqlCell::new(5);
+    ///     assert_eq!(c.get(), 5);
+    /// } // BQL could be unlocked
+    /// ```
+    pub fn new() -> Self {
+        if !is_locked() {
+            lock();
+            Self { locked: true }
+        } else {
+            block_unlock(true);
+            Self { locked: false }
+        }
+    }
+}
+
+impl Default for BqlGuard {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+impl Drop for BqlGuard {
+    fn drop(&mut self) {
+        if self.locked {
+            unlock();
+        } else {
+            block_unlock(false);
+        }
+    }
+}
+
+/// Executes a closure (function) within the BQL context.
+///
+/// This function creates a `BqlGuard`.
+///
+/// # Examples
+///
+/// ```should_panic
+/// use bql::{with_guard, BqlRefCell};
+///
+/// let c = BqlRefCell::new(5);
+///
+/// with_guard(|| {
+///     // BQL is locked
+///     let m = c.borrow();
+///
+///     assert_eq!(*m, 5);
+/// }); // BQL could be unlocked
+///
+/// let b = c.borrow(); // this causes a panic
+/// ```
+pub fn with_guard<F, R>(f: F) -> R
+where
+    F: FnOnce() -> R,
+{
+    let _guard = BqlGuard::new();
+    f()
+}
diff --git a/stubs/iothread-lock.c b/stubs/iothread-lock.c
index c89c9c7228f3..e2ebce565a06 100644
--- a/stubs/iothread-lock.c
+++ b/stubs/iothread-lock.c
@@ -23,6 +23,17 @@ void bql_unlock(void)
     assert(!bql_unlock_blocked);
 }
 
+void rust_bql_lock(void)
+{
+    rust_bql_mock_lock();
+}
+
+void rust_bql_unlock(void)
+{
+    bql_unlock();
+    bql_is_locked = false;
+}
+
 void bql_block_unlock(bool increase)
 {
     uint32_t new_value;
diff --git a/system/cpus.c b/system/cpus.c
index ef2d2f241faa..da31bda59444 100644
--- a/system/cpus.c
+++ b/system/cpus.c
@@ -578,6 +578,11 @@ void bql_lock_impl(const char *file, int line)
     bql_lock_fn(&bql, file, line);
 }
 
+void rust_bql_lock(void)
+{
+    bql_lock();
+}
+
 void bql_unlock(void)
 {
     g_assert(bql_locked());
@@ -585,6 +590,11 @@ void bql_unlock(void)
     qemu_mutex_unlock(&bql);
 }
 
+void rust_bql_unlock(void)
+{
+    bql_unlock();
+}
+
 void qemu_cond_wait_bql(QemuCond *cond)
 {
     qemu_cond_wait(cond, &bql);
-- 
2.34.1



  parent reply	other threads:[~2025-11-13  4:58 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-11-13  5:19 [PATCH 00/22] rust/hpet: Move towards lockless IO, partly Zhao Liu
2025-11-13  5:19 ` [PATCH 01/22] rust/migration: Add Sync implementation for Migratable<> Zhao Liu
2025-11-13  5:19 ` [PATCH 02/22] rust/migration: Fix missing name in the VMSD of Migratable<> Zhao Liu
2025-11-13  5:19 ` [PATCH 03/22] rust/migration: Check name field in VMStateDescriptionBuilder Zhao Liu
2025-11-13  5:19 ` Zhao Liu [this message]
2025-11-13  5:19 ` [PATCH 05/22] rust/bql: Ensure BQL locked early at BqlRefCell borrowing Zhao Liu
2025-11-13  5:19 ` [PATCH 06/22] rust/memory: Add enable_lockless_io binding Zhao Liu
2025-11-13  5:19 ` [PATCH 07/22] rust/hpet: Reduce unnecessary mutable self argument Zhao Liu
2025-11-13  5:19 ` [PATCH 08/22] rust/hpet: Rename HPETRegister to DecodedRegister Zhao Liu
2025-11-13  5:19 ` [PATCH 09/22] rust/hpet: Rename decoded "reg" enumeration to "target" Zhao Liu
2025-11-13  5:19 ` [PATCH 10/22] rust/hpet: Abstract HPETTimerRegisters struct Zhao Liu
2025-11-13 11:24   ` Paolo Bonzini
2025-11-14  4:37     ` Zhao Liu
2025-11-15  7:54       ` Paolo Bonzini
2025-11-13  5:19 ` [PATCH 11/22] rust/hpet: Make timer register accessors as methods of HPETTimerRegisters Zhao Liu
2025-11-13  5:19 ` [PATCH 12/22] rust/hpet: Abstract HPETRegisters struct Zhao Liu
2025-11-13  5:19 ` [PATCH 13/22] rust/hpet: Make global register accessors as methods of HPETRegisters Zhao Liu
2025-11-13  5:19 ` [PATCH 14/22] rust/hpet: Borrow HPETState.regs once in HPETState::post_load() Zhao Liu
2025-11-13  5:19 ` [PATCH 15/22] rust/hpet: Explicitly initialize complex fields in init() Zhao Liu
2025-11-13  5:19 ` [PATCH 16/22] rust/hpet: Pass &BqlRefCell<HPETRegisters> as argument during MMIO access Zhao Liu
2025-11-13  5:19 ` [PATCH 17/22] rust/hpet: Maintain HPETTimerRegisters in HPETRegisters Zhao Liu
2025-11-13  5:19 ` [PATCH 18/22] rust/hpet: Borrow BqlRefCell<HPETRegisters> at top level Zhao Liu
2025-11-13  5:19 ` [PATCH 19/22] rust/hpet: Rename hpet_regs variables to regs Zhao Liu
2025-11-13  5:19 ` [PATCH 20/22] rust/hpet: Apply Migratable<> wrapper and ToMigrationState for HPETRegisters Zhao Liu
2025-11-13  5:19 ` [PATCH 21/22] rust/hpet: Replace BqlRefCell<HPETRegisters> with Mutex<HPETRegisters> Zhao Liu
2025-11-13  9:31   ` Zhao Liu
2025-11-13 11:36     ` Zhao Liu
2025-11-13  5:19 ` [PATCH 22/22] rust/hpet: Enable lockless IO Zhao Liu
2025-11-13 14:29   ` Paolo Bonzini
2025-11-14  6:39     ` Zhao Liu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20251113051937.4017675-5-zhao1.liu@intel.com \
    --to=zhao1.liu@intel.com \
    --cc=imammedo@redhat.com \
    --cc=manos.pitsidianakis@linaro.org \
    --cc=marcandre.lureau@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=qemu-rust@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).