qemu-rust.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Paolo Bonzini <pbonzini@redhat.com>
To: "Zhao Liu" <zhao1.liu@intel.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
Subject: Re: [PATCH 10/22] rust/hpet: Abstract HPETTimerRegisters struct
Date: Thu, 13 Nov 2025 12:24:07 +0100	[thread overview]
Message-ID: <12e93226-b70d-4c9c-bf8a-db7e0e05b585@redhat.com> (raw)
In-Reply-To: <20251113051937.4017675-11-zhao1.liu@intel.com>

On 11/13/25 06:19, Zhao Liu wrote:
> Place all timer N's registers in a HPETTimerRegisters struct.
> 
> This allows all Timer N registers to be grouped together with global
> registers and managed using a single lock (BqlRefCell or Mutex) in
> future. And this makes it easier to apply ToMigrationState macro.

This is pretty much the crucial patch in the series and it's the only
one that needs more work, or some fixup at the end.

In particular, more fields of HPETTimer need to be moved to the
HPETTimerRegisters.  It's possible that it would be a problem to move
the timer itself inside the mutex but, at least, the HPETTimer could be
changed to just

pub struct HPETTimer {
     timer: QemuTimer,
     state: NonNull<HPETState>,
     index: u8,
}

as in the patch included at the end (compile-tested only).  Then, the
BqlRefCell<HPETTimer> can be changed to just HPETTimer because all the
fields handle their interior-mutable fields.

Preserving the old migration format can then be solved in two ways:

1) with a handwritten ToMigrationStateShared implementation for
HPETTimer (and marking the tn_regs array as #[migration_state(omit)])

2) by also adding num_timers_save and the timer's expiration to
HPETRegisters and HPETTimerRegisters, respectively.

I think I prefer the former, but I haven't written the code so it's
not my choice. :)

I'm okay with doing these changes on top of these patches as well.

Paolo

diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs
index 75a6fd8a050..5ff02c01539 100644
--- a/rust/hw/timer/hpet/src/device.rs
+++ b/rust/hw/timer/hpet/src/device.rs
@@ -172,7 +172,7 @@ const fn deactivating_bit(old: u64, new: u64, shift: usize) -> bool {
  }
  
  fn timer_handler(timer_cell: &BqlRefCell<HPETTimer>) {
-    let mut t = timer_cell.borrow_mut();
+    let t = timer_cell.borrow();
      // SFAETY: state field is valid after timer initialization.
      let mut regs = unsafe { t.state.as_ref() }.regs.lock().unwrap();
      t.callback(&mut regs)
@@ -181,6 +181,9 @@ fn timer_handler(timer_cell: &BqlRefCell<HPETTimer>) {
  #[repr(C)]
  #[derive(Debug, Default, ToMigrationState)]
  pub struct HPETTimerRegisters {
+    // Only needed for migration
+    index: u8,
+
      // Memory-mapped, software visible timer registers
      /// Timer N Configuration and Capability Register
      config: u64,
@@ -188,9 +191,35 @@ pub struct HPETTimerRegisters {
      cmp: u64,
      /// Timer N FSB Interrupt Route Register
      fsb: u64,
+
+    // Hidden register state
+    /// comparator (extended to counter width)
+    cmp64: u64,
+    /// Last value written to comparator
+    period: u64,
+    /// timer pop will indicate wrap for one-shot 32-bit
+    /// mode. Next pop will be actual timer expiration.
+    wrap_flag: u8,
+    /// last value armed, to avoid timer storms
+    last: u64,
  }
  
  impl HPETTimerRegisters {
+    /// calculate next value of the general counter that matches the
+    /// target (either entirely, or the low 32-bit only depending on
+    /// the timer mode).
+    fn update_cmp64(&mut self, cur_tick: u64) {
+        self.cmp64 = if self.is_32bit_mod() {
+            let mut result: u64 = cur_tick.deposit(0, 32, self.cmp);
+            if result < cur_tick {
+                result += 0x100000000;
+            }
+            result
+        } else {
+            self.cmp
+        }
+    }
+
      const fn is_fsb_route_enabled(&self) -> bool {
          self.config & (1 << HPET_TN_CFG_FSB_ENABLE_SHIFT) != 0
      }
@@ -235,17 +264,6 @@ pub struct HPETTimer {
      qemu_timer: Timer,
      /// timer block abstraction containing this timer
      state: NonNull<HPETState>,
-
-    // Hidden register state
-    /// comparator (extended to counter width)
-    cmp64: u64,
-    /// Last value written to comparator
-    period: u64,
-    /// timer pop will indicate wrap for one-shot 32-bit
-    /// mode. Next pop will be actual timer expiration.
-    wrap_flag: u8,
-    /// last value armed, to avoid timer storms
-    last: u64,
  }
  
  // SAFETY: Sync is not automatically derived due to the `state` field,
@@ -260,10 +278,6 @@ fn new(index: u8, state: *const HPETState) -> HPETTimer {
              // is initialized below.
              qemu_timer: unsafe { Timer::new() },
              state: NonNull::new(state.cast_mut()).unwrap(),
-            cmp64: 0,
-            period: 0,
-            wrap_flag: 0,
-            last: 0,
          }
      }
  
@@ -291,22 +305,6 @@ fn is_int_active(&self, regs: &HPETRegisters) -> bool {
          regs.is_timer_int_active(self.index.into())
      }
  
-    /// calculate next value of the general counter that matches the
-    /// target (either entirely, or the low 32-bit only depending on
-    /// the timer mode).
-    fn calculate_cmp64(&self, regs: &MutexGuard<HPETRegisters>, cur_tick: u64, target: u64) -> u64 {
-        let tn_regs = &regs.tn_regs[self.index as usize];
-        if tn_regs.is_32bit_mod() {
-            let mut result: u64 = cur_tick.deposit(0, 32, target);
-            if result < cur_tick {
-                result += 0x100000000;
-            }
-            result
-        } else {
-            target
-        }
-    }
-
      fn get_int_route(&self, regs: &MutexGuard<HPETRegisters>) -> usize {
          if self.index <= 1 && regs.is_legacy_mode() {
              // If LegacyReplacement Route bit is set, HPET specification requires
@@ -371,35 +369,34 @@ fn update_irq(&self, regs: &mut MutexGuard<HPETRegisters>, set: bool) {
          self.set_irq(regs, set);
      }
  
-    fn arm_timer(&mut self, regs: &MutexGuard<HPETRegisters>, tick: u64) {
-        let tn_regs = &regs.tn_regs[self.index as usize];
+    fn arm_timer(&self, tn_regs: &mut HPETTimerRegisters, tick: u64) {
          let mut ns = self.get_state().get_ns(tick);
  
          // Clamp period to reasonable min value (1 us)
-        if tn_regs.is_periodic() && ns - self.last < 1000 {
-            ns = self.last + 1000;
+        if tn_regs.is_periodic() && ns - tn_regs.last < 1000 {
+            ns = tn_regs.last + 1000;
          }
  
-        self.last = ns;
-        self.qemu_timer.modify(self.last);
+        tn_regs.last = ns;
+        self.qemu_timer.modify(tn_regs.last);
      }
  
-    fn set_timer(&mut self, regs: &MutexGuard<HPETRegisters>) {
-        let tn_regs = &regs.tn_regs[self.index as usize];
+    fn set_timer(&self, regs: &mut MutexGuard<HPETRegisters>) {
+        let tn_regs = &mut regs.tn_regs[self.index as usize];
          let cur_tick: u64 = self.get_state().get_ticks();
  
-        self.wrap_flag = 0;
-        self.cmp64 = self.calculate_cmp64(regs, cur_tick, tn_regs.cmp);
+        tn_regs.wrap_flag = 0;
+        tn_regs.update_cmp64(cur_tick);
          if tn_regs.is_32bit_mod() {
              // HPET spec says in one-shot 32-bit mode, generate an interrupt when
              // counter wraps in addition to an interrupt with comparator match.
-            if !tn_regs.is_periodic() && self.cmp64 > hpet_next_wrap(cur_tick) {
-                self.wrap_flag = 1;
-                self.arm_timer(regs, hpet_next_wrap(cur_tick));
+            if !tn_regs.is_periodic() && tn_regs.cmp64 > hpet_next_wrap(cur_tick) {
+                tn_regs.wrap_flag = 1;
+                self.arm_timer(tn_regs, hpet_next_wrap(cur_tick));
                  return;
              }
          }
-        self.arm_timer(regs, self.cmp64);
+        self.arm_timer(tn_regs, tn_regs.cmp64);
      }
  
      fn del_timer(&self, regs: &mut MutexGuard<HPETRegisters>) {
@@ -440,7 +437,7 @@ fn prepare_tn_cfg_reg_new(
  
      /// Configuration and Capability Register
      fn set_tn_cfg_reg(
-        &mut self,
+        &self,
          regs: &mut MutexGuard<HPETRegisters>,
          shift: u32,
          len: u32,
@@ -462,7 +459,7 @@ fn set_tn_cfg_reg(
          let tn_regs = &mut regs.tn_regs[self.index as usize];
          if tn_regs.is_32bit_mod() {
              tn_regs.cmp = u64::from(tn_regs.cmp as u32); // truncate!
-            self.period = u64::from(self.period as u32); // truncate!
+            tn_regs.period = u64::from(tn_regs.period as u32); // truncate!
          }
  
          if regs.is_hpet_enabled() {
@@ -472,7 +469,7 @@ fn set_tn_cfg_reg(
  
      /// Comparator Value Register
      fn set_tn_cmp_reg(
-        &mut self,
+        &self,
          regs: &mut MutexGuard<HPETRegisters>,
          shift: u32,
          len: u32,
@@ -499,7 +496,7 @@ fn set_tn_cmp_reg(
          }
  
          if tn_regs.is_periodic() {
-            self.period = self.period.deposit(shift, length, value);
+            tn_regs.period = tn_regs.period.deposit(shift, length, value);
          }
  
          tn_regs.clear_valset();
@@ -520,10 +517,11 @@ fn set_tn_fsb_route_reg(
          tn_regs.fsb = tn_regs.fsb.deposit(shift, len, val);
      }
  
-    fn reset(&mut self, regs: &mut MutexGuard<HPETRegisters>) {
+    fn reset(&self, regs: &mut MutexGuard<HPETRegisters>) {
          self.del_timer(regs);
  
          let tn_regs = &mut regs.tn_regs[self.index as usize];
+        tn_regs.index = self.index;
          tn_regs.cmp = u64::MAX; // Comparator Match Registers reset to all 1's.
          tn_regs.config = (1 << HPET_TN_CFG_PERIODIC_CAP_SHIFT) | (1 << HPET_TN_CFG_SIZE_CAP_SHIFT);
          if self.get_state().has_msi_flag() {
@@ -532,29 +530,28 @@ fn reset(&mut self, regs: &mut MutexGuard<HPETRegisters>) {
          // advertise availability of ioapic int
          tn_regs.config |=
              (u64::from(self.get_state().int_route_cap)) << HPET_TN_CFG_INT_ROUTE_CAP_SHIFT;
-        self.period = 0;
-        self.wrap_flag = 0;
+        tn_regs.period = 0;
+        tn_regs.wrap_flag = 0;
      }
  
      /// timer expiration callback
-    fn callback(&mut self, regs: &mut MutexGuard<HPETRegisters>) {
+    fn callback(&self, regs: &mut MutexGuard<HPETRegisters>) {
          let tn_regs = &mut regs.tn_regs[self.index as usize];
-        let period: u64 = self.period;
          let cur_tick: u64 = self.get_state().get_ticks();
  
-        if tn_regs.is_periodic() && period != 0 {
-            while hpet_time_after(cur_tick, self.cmp64) {
-                self.cmp64 += period;
+        if tn_regs.is_periodic() && tn_regs.period != 0 {
+            while hpet_time_after(cur_tick, tn_regs.cmp64) {
+                tn_regs.cmp64 += tn_regs.period;
              }
              if tn_regs.is_32bit_mod() {
-                tn_regs.cmp = u64::from(self.cmp64 as u32); // truncate!
+                tn_regs.cmp = u64::from(tn_regs.cmp64 as u32); // truncate!
              } else {
-                tn_regs.cmp = self.cmp64;
+                tn_regs.cmp = tn_regs.cmp64;
              }
-            self.arm_timer(regs, self.cmp64);
-        } else if self.wrap_flag != 0 {
-            self.wrap_flag = 0;
-            self.arm_timer(regs, self.cmp64);
+            self.arm_timer(tn_regs, tn_regs.cmp64);
+        } else if tn_regs.wrap_flag != 0 {
+            tn_regs.wrap_flag = 0;
+            self.arm_timer(tn_regs, tn_regs.cmp64);
          }
          self.update_irq(regs, true);
      }
@@ -571,7 +568,7 @@ fn read(&self, target: TimerRegister, regs: &MutexGuard<HPETRegisters>) -> u64 {
      }
  
      fn write(
-        &mut self,
+        &self,
          target: TimerRegister,
          regs: &mut MutexGuard<HPETRegisters>,
          value: u64,
@@ -731,7 +728,7 @@ fn set_cfg_reg(&self, regs: &mut MutexGuard<HPETRegisters>, shift: u32, len: u32
              for timer in self.timers.iter().take(self.num_timers) {
                  // Protect timer in lockless IO case which would not lock BQL.
                  bql::with_guard(|| {
-                    let mut t = timer.borrow_mut();
+                    let t = timer.borrow_mut();
                      let id = t.index as usize;
                      let tn_regs = &regs.tn_regs[id];
  
@@ -992,15 +989,14 @@ fn pre_save(&self) -> Result<(), migration::Infallible> {
      }
  
      fn post_load(&self, _version_id: u8) -> Result<(), migration::Infallible> {
-        let regs = self.regs.lock().unwrap();
+        let mut regs = self.regs.lock().unwrap();
+        let cnt = regs.counter;
  
-        for timer in self.timers.iter().take(self.num_timers) {
-            let mut t = timer.borrow_mut();
-            let cnt = regs.counter;
-            let cmp = regs.tn_regs[t.index as usize].cmp;
+        for index in 0..self.num_timers {
+            let tn_regs = &mut regs.tn_regs[index];
  
-            t.cmp64 = t.calculate_cmp64(&regs, cnt, cmp);
-            t.last = CLOCK_VIRTUAL.get_ns() - NANOSECONDS_PER_SECOND;
+            tn_regs.update_cmp64(cnt);
+            tn_regs.last = CLOCK_VIRTUAL.get_ns() - NANOSECONDS_PER_SECOND;
          }
  
          // Recalculate the offset between the main counter and guest time
@@ -1072,9 +1068,12 @@ impl ObjectImpl for HPETState {
          .version_id(1)
          .minimum_version_id(1)
          .fields(vmstate_fields! {
+            vmstate_of!(HPETTimerRegistersMigration, index),
              vmstate_of!(HPETTimerRegistersMigration, config),
              vmstate_of!(HPETTimerRegistersMigration, cmp),
              vmstate_of!(HPETTimerRegistersMigration, fsb),
+            vmstate_of!(HPETTimerRegistersMigration, period),
+            vmstate_of!(HPETTimerRegistersMigration, wrap_flag),
          })
          .build()
  );
@@ -1085,9 +1084,6 @@ impl ObjectImpl for HPETState {
          .version_id(2)
          .minimum_version_id(2)
          .fields(vmstate_fields! {
-            vmstate_of!(HPETTimer, index),
-            vmstate_of!(HPETTimer, period),
-            vmstate_of!(HPETTimer, wrap_flag),
              vmstate_of!(HPETTimer, qemu_timer),
          })
          .build();



  reply	other threads:[~2025-11-13 11:25 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 ` [PATCH 04/22] rust/bql: Add BqlGuard to provide BQL context Zhao Liu
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 [this message]
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=12e93226-b70d-4c9c-bf8a-db7e0e05b585@redhat.com \
    --to=pbonzini@redhat.com \
    --cc=imammedo@redhat.com \
    --cc=manos.pitsidianakis@linaro.org \
    --cc=marcandre.lureau@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=qemu-rust@nongnu.org \
    --cc=zhao1.liu@intel.com \
    /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).