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]> --- 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
