On Thu, Sep 10, 2026 at 10:39:40PM +0800, TANG Tiancheng wrote:
> Raw host ticks keep advancing while the VM is stopped. Use
> cpus_get_elapsed_ticks() for cycles and non-icount instruction counting,
> and retain icount_get_raw() for instructions under icount. Document that
> cpu_get_ticks() returns its stored value while VM ticks are disabled.
> 
> Under icount, cycles are already virtual nanoseconds. Convert only raw
> instruction counts when scheduling overflow, avoiding a second scaling
> of cycle distances. Add a cycle-overflow regression with icount shift=3.
> 
> Link: https://lists.nongnu.org/archive/html/qemu-devel/2025-10/msg00668.html
> Signed-off-by: TANG Tiancheng <[email protected]>
> Reviewed-by: Daniel Henrique Barboza <[email protected]>
Reviewed-by: Chao Liu <[email protected]>

Thanks,
Chao

> ---
>  system/cpu-timers.c                         |  4 +-
>  system/cpus.c                               |  6 +--
>  target/riscv/tcg/csr.c                      |  8 +---
>  target/riscv/tcg/pmu.c                      | 52 +++++++++++++++-----------
>  target/riscv/tcg/pmu.h                      |  1 +
>  tests/tcg/riscv64/sscofpmf-cycle-overflow.S | 58 
> +++++++++++++++++++++++++++++
>  tests/tcg/riscv64/system/meson.build        |  7 ++++
>  7 files changed, 103 insertions(+), 33 deletions(-)
> 
> diff --git a/system/cpu-timers.c b/system/cpu-timers.c
> index 
> 9919b46230f1caf8be1a1b6ef00acd94678437ce..0415636aff3f774f0de61fdac3969f5b45ae6993
>  100644
> --- a/system/cpu-timers.c
> +++ b/system/cpu-timers.c
> @@ -118,8 +118,8 @@ void cpu_enable_ticks(void)
>  }
>  
>  /*
> - * disable cpu_get_ticks() : the clock is stopped. You must not call
> - * cpu_get_ticks() after that.
> + * Freeze VM ticks. While disabled, cpu_get_ticks() returns the stored tick
> + * value instead of sampling the advancing host counter.
>   * Caller must hold BQL which serves as mutex for vm_clock_seqlock.
>   */
>  void cpu_disable_ticks(void)
> diff --git a/system/cpus.c b/system/cpus.c
> index 
> e11a5aab6a696962d94ad30ac38acd8867b1bf88..f61639ae78277fd90cbddb0b9f75b134e3cc1a17
>  100644
> --- a/system/cpus.c
> +++ b/system/cpus.c
> @@ -237,9 +237,9 @@ void cpus_set_virtual_clock(int64_t new_time)
>  }
>  
>  /*
> - * return the time elapsed in VM between vm_start and vm_stop.  Unless
> - * icount is active, cpus_get_elapsed_ticks() uses units of the host CPU 
> cycle
> - * counter.
> + * Return VM-elapsed ticks. While VM ticks are disabled, passage of host time
> + * does not advance the returned value. Unless icount is active, the units 
> are
> + * those of the host CPU cycle counter.
>   */
>  int64_t cpus_get_elapsed_ticks(void)
>  {
> diff --git a/target/riscv/tcg/csr.c b/target/riscv/tcg/csr.c
> index 
> d15a2d096cb6e13cd123ff9ae82ee7c643c2a961..60caee32dc0cf5b6a8492e0cf8ff15f71acc2087
>  100644
> --- a/target/riscv/tcg/csr.c
> +++ b/target/riscv/tcg/csr.c
> @@ -1327,13 +1327,7 @@ static uint64_t 
> riscv_pmu_ctr_get_fixed_counters_val(CPURISCVState *env,
>      }
>  
>      if (!cfg_val) {
> -        if (icount_enabled()) {
> -                curr_val = inst ? icount_get_raw() : icount_get();
> -        } else {
> -            curr_val = cpu_get_host_ticks();
> -        }
> -
> -        return curr_val;
> +        return riscv_pmu_read_fixed_source(env, inst);
>      }
>  
>      /* Update counter before reading. */
> diff --git a/target/riscv/tcg/pmu.c b/target/riscv/tcg/pmu.c
> index 
> f19f417e90e33a94d00007ef132ef4e154175b19..ea0ffe41258d4dbef9dc952655e6301c14ba1d23
>  100644
> --- a/target/riscv/tcg/pmu.c
> +++ b/target/riscv/tcg/pmu.c
> @@ -24,8 +24,14 @@
>  #include "pmu.h"
>  #include "exec/icount.h"
>  #include "system/device_tree.h"
> +#include "system/cpu-timers.h"
>  
> -#define RISCV_TIMEBASE_FREQ 1000000000 /* 1Ghz */
> +/*
> + * cpu_get_ticks() does not expose the host tick frequency.  Use a 1 GHz
> + * approximation only when scheduling non-icount overflow checks; fixed
> + * counter values remain in host-tick units.
> + */
> +#define RISCV_PMU_HOST_TICK_HZ_ASSUMED 1000000000
>  
>  static bool riscv_pmu_counter_valid(RISCVCPU *cpu, uint32_t ctr_idx)
>  {
> @@ -75,6 +81,19 @@ static bool riscv_pmu_counter_filtered(CPURISCVState *env, 
> uint64_t cfg)
>              (cfg & MHPMEVENT_BIT_UINH));
>  }
>  
> +/*
> + * VM-elapsed ticks stop advancing while VM ticks are disabled.  Under
> + * icount, instruction events retain raw instruction-count units.
> + */
> +uint64_t riscv_pmu_read_fixed_source(CPURISCVState *env, bool instret)
> +{
> +    if (instret && icount_enabled()) {
> +        return icount_get_raw();
> +    }
> +
> +    return cpus_get_elapsed_ticks();
> +}
> +
>  /*
>   * Information needed to update counters:
>   *  new_priv, new_virt: To correctly save starting snapshot for the newly
> @@ -96,11 +115,7 @@ static void riscv_pmu_icount_update_priv(CPURISCVState 
> *env,
>      uint64_t *counter_arr;
>      uint64_t delta;
>  
> -    if (icount_enabled()) {
> -        current_icount = icount_get_raw();
> -    } else {
> -        current_icount = cpu_get_host_ticks();
> -    }
> +    current_icount = riscv_pmu_read_fixed_source(env, true);
>  
>      if (env->virt_enabled) {
>          g_assert(env->priv <= PRV_S);
> @@ -137,11 +152,7 @@ static void riscv_pmu_cycle_update_priv(CPURISCVState 
> *env,
>      uint64_t *counter_arr;
>      uint64_t delta;
>  
> -    if (icount_enabled()) {
> -        current_ticks = icount_get();
> -    } else {
> -        current_ticks = cpu_get_host_ticks();
> -    }
> +    current_ticks = riscv_pmu_read_fixed_source(env, false);
>  
>      if (env->virt_enabled) {
>          g_assert(env->priv <= PRV_S);
> @@ -286,17 +297,15 @@ static bool riscv_pmu_event_supported(uint32_t 
> event_idx)
>      }
>  }
>  
> -static int64_t pmu_icount_ticks_to_ns(int64_t value)
> +static int64_t pmu_ticks_to_ns(CPURISCVState *env, uint32_t ctr_idx,
> +                               int64_t value)
>  {
> -    int64_t ret = 0;
> -
> -    if (icount_enabled()) {
> -        ret = icount_to_ns(value);
> -    } else {
> -        ret = (NANOSECONDS_PER_SECOND / RISCV_TIMEBASE_FREQ) * value;
> +    if (icount_enabled() &&
> +        riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) {
> +        return icount_to_ns(value);
>      }
>  
> -    return ret;
> +    return (NANOSECONDS_PER_SECOND / RISCV_PMU_HOST_TICK_HZ_ASSUMED) * value;
>  }
>  
>  void riscv_pmu_rebuild_event_map(CPURISCVState *env)
> @@ -448,8 +457,9 @@ int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t 
> value, uint32_t ctr_idx)
>  
>      if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) ||
>          riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) {
> -        overflow_ns = pmu_icount_ticks_to_ns((int64_t)overflow_delta);
> -        overflow_left = pmu_icount_ticks_to_ns(overflow_left) ;
> +        overflow_ns = pmu_ticks_to_ns(env, ctr_idx,
> +                                      (int64_t)overflow_delta);
> +        overflow_left = pmu_ticks_to_ns(env, ctr_idx, overflow_left);
>      } else {
>          return -1;
>      }
> diff --git a/target/riscv/tcg/pmu.h b/target/riscv/tcg/pmu.h
> index 
> 910091690290cac9f77855f479bb9d90b2762efe..339a4b3ac09c4a91cddd9250824284203b16b4fa
>  100644
> --- a/target/riscv/tcg/pmu.h
> +++ b/target/riscv/tcg/pmu.h
> @@ -26,6 +26,7 @@ bool riscv_pmu_ctr_monitor_instructions(CPURISCVState *env,
>                                          uint32_t target_ctr);
>  bool riscv_pmu_ctr_monitor_cycles(CPURISCVState *env,
>                                    uint32_t target_ctr);
> +uint64_t riscv_pmu_read_fixed_source(CPURISCVState *env, bool instret);
>  void riscv_pmu_timer_cb(void *priv);
>  void riscv_pmu_init(RISCVCPU *cpu, Error **errp);
>  void riscv_pmu_rebuild_event_map(CPURISCVState *env);
> diff --git a/tests/tcg/riscv64/sscofpmf-cycle-overflow.S 
> b/tests/tcg/riscv64/sscofpmf-cycle-overflow.S
> new file mode 100644
> index 
> 0000000000000000000000000000000000000000..846d4651c4ee8f06df83bed85512ab9794552c28
> --- /dev/null
> +++ b/tests/tcg/riscv64/sscofpmf-cycle-overflow.S
> @@ -0,0 +1,58 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +
> +     .option norvc
> +     .option norelax
> +
> +     .text
> +     .global _start
> +_start:
> +     /* UINT64_MAX - 4095 leaves 4096 cycle increments until overflow. */
> +     csrw    mhpmevent3, zero
> +     li      t0, -4096
> +     csrw    mhpmcounter3, t0
> +     li      t0, 1
> +     csrw    mhpmevent3, t0          /* mhpmevent3: cycles */
> +     csrr    t1, mcycle
> +
> +1:
> +     csrr    t0, mhpmevent3
> +     beqz    t0, fail
> +     li      t2, 1
> +     slli    t2, t2, 63
> +     and     t0, t0, t2
> +     bnez    t0, pass
> +
> +     /*
> +      * Allow 16384 cycles for OF to become visible. With shift=3, scaling
> +      * the 4096-cycle distance twice would delay it to about 32768 cycles.
> +      */
> +     csrr    t0, mcycle
> +     sub     t0, t0, t1
> +     li      t2, 16384
> +     bltu    t0, t2, 1b
> +
> +fail:
> +     li      a0, 1
> +     j       exit
> +
> +pass:
> +     li      a0, 0
> +
> +exit:
> +     lla     a1, semiargs
> +     li      t0, 0x20026             /* ADP_Stopped_ApplicationExit */
> +     sd      t0, 0(a1)
> +     sd      a0, 8(a1)
> +     li      a0, 0x20                /* TARGET_SYS_EXIT_EXTENDED */
> +
> +     /* Semihosting call sequence. */
> +     .balign 16
> +     slli    zero, zero, 0x1f
> +     ebreak
> +     srai    zero, zero, 0x7
> +     j       .
> +
> +     .data
> +     .balign 16
> +semiargs:
> +     .space  16
> diff --git a/tests/tcg/riscv64/system/meson.build 
> b/tests/tcg/riscv64/system/meson.build
> index 
> ebe78200fd551b42d3c99ae19ca03803797f803d..668a9a76070b6f16087b08ea84683d883312e951
>  100644
> --- a/tests/tcg/riscv64/system/meson.build
> +++ b/tests/tcg/riscv64/system/meson.build
> @@ -68,6 +68,13 @@ tests += {
>    },
>  }
>  
> +tests += {
> +  'sscofpmf-cycle-overflow.S': {
> +    'cflags': cflags,
> +    'qemu_args': ['-cpu', 'max', '-icount', 'shift=3', qemu_args],
> +  },
> +}
> +
>  if 'qemu-system-riscv64' in emulators
>    tcg_tests += {
>      'riscv64-softmmu': {
> 
> -- 
> 2.43.0
> 

Reply via email to