[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v5 2/2] x86/time: avoid early uses of NOW() to return zero



On Wed, Aug 19, 2026 at 01:45:56PM +0200, Jan Beulich wrote:
> Waiting loops like the one in flush_command_buffer() will degenerate to
> infinite ones when used early enough for NOW() to still return constant
> zero. Make sure the returned value at least monotonically increases. When
> available, use nominal frequency values as initial approximation.
> 
> Do this only in get_s_time(), as producing a sane value in
> get_s_time_fixed() for non-zero inputs won't be reasonably possible.
> Put an assertion there.
> 
> Reported-by: Roger Pau Monné <roger.pau@xxxxxxxxxx>
> Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>

Acked-by: Roger Pau Monné <roger@xxxxxxxxxxxxxx>

One minor comment below.

> ---
> RFC: While generally the mentioned waiting loops will take longer to time
>      out, on a very fast CPU tight loops may time out too early.
> 
> With "x86/time: set AP's TSC scale estimate earlier" the counter update
> may not need to be atomic anymore, as then only the BSP can reasonably hit
> that path.
> 
> I don't think Fixes: tags should be put here. If we did, we'd have to
> enumerate all introductions of early uses of NOW() (or get_s_time()), with
> the exception of those dealing with getting back 0 (which I expect is only
> printk_start_of_line()). Will want backporting nevertheless (unless deemed
> too risky).

I agree with backporting, current behavior of getting stuck in an
infinite loop is worse than timing out earlier than expected (which is
the worse outcome of this patch).

> ---
> v5: Move addition to early_cpu_init() down. Adjust commentary there.
> v3: Use "high" / "max" freq if "nominal" isn't available. Set NOW_good.
> v2: Add assertion to get_s_time_fixed(). Use nominal frequencies for very
>     early setting, if available.
> 
> --- a/xen/arch/x86/cpu/common.c
> +++ b/xen/arch/x86/cpu/common.c
> @@ -19,6 +19,7 @@
>  #include <asm/random.h>
>  #include <asm/setup.h>
>  #include <asm/shstk.h>
> +#include <asm/time.h>
>  #include <asm/xstate.h>
>  
>  #include <public/sysctl.h>
> @@ -444,6 +445,39 @@ void __init early_cpu_init(bool verbose)
>  
>       if (!(c->vendor & (X86_VENDOR_AMD | X86_VENDOR_HYGON)))
>               park_offline_cpus = opt_mce;
> +
> +     /*
> +      * If nominal freq isn't available, use highest, thus causing NOW()
> +      * output to move more slowly.  See preset_tsc_scale().
> +      */
> +     if (c->cpuid_level >= 0x15) {
> +             cpuid(0x15, &eax, &ebx, &ecx, &edx);
> +
> +             if (ecx && ebx && eax)
> +                     preset_tsc_scale(DIV_ROUND_UP(ecx * 1UL * ebx, eax));
> +             else if (c->cpuid_level >= 0x16) {
> +                     /* Assume CPU base freq ≈ TSC freq. */
> +                     cpuid(0x16, &eax, &ebx, &ecx, &edx);
> +                     if (eax)
> +                             preset_tsc_scale(eax * 1000000UL);
> +                     else if (ebx)
> +                             preset_tsc_scale(ebx * 1000000UL);
> +             }
> +     } else if (c->vendor & (X86_VENDOR_AMD | X86_VENDOR_HYGON)) {
> +             unsigned int nom_mhz = 0, hi_mhz = 0;
> +
> +             amd_process_freq(c, NULL, &nom_mhz, &hi_mhz);
> +             if (nom_mhz)
> +                     preset_tsc_scale(nom_mhz * 1000000UL);
> +             else if (hi_mhz)
> +                     preset_tsc_scale(hi_mhz * 1000000UL);
> +     } else if (c->vendor & X86_VENDOR_INTEL) {
> +             unsigned int hi_mhz = 0;
> +
> +             intel_process_freq(c, NULL, &hi_mhz);
> +             if (hi_mhz)
> +                     preset_tsc_scale(hi_mhz * 1000000UL);
> +     }
>  }
>  
>  void reset_cpuinfo(struct cpuinfo_x86 *c, bool keep_basic)
> --- a/xen/arch/x86/include/asm/time.h
> +++ b/xen/arch/x86/include/asm/time.h
> @@ -23,6 +23,7 @@ mktime (unsigned int year, unsigned int
>  int time_suspend(void);
>  int time_resume(void);
>  
> +void preset_tsc_scale(unsigned long freq);
>  void init_percpu_time(void);
>  void time_latch_stamps(void);
>  
> --- a/xen/arch/x86/cpu/intel.c
> +++ b/xen/arch/x86/cpu/intel.c
> @@ -476,8 +476,8 @@ static int num_cpu_cores(struct cpuinfo_
>               return 1;
>  }
>  
> -static void intel_process_freq(const struct cpuinfo_x86 *c,
> -                               unsigned int *min_mhz, unsigned int *max_mhz)
> +void intel_process_freq(const struct cpuinfo_x86 *c,
> +                        unsigned int *min_mhz, unsigned int *max_mhz)
>  {
>      uint64_t msrval;
>      uint8_t max_ratio, min_ratio;
> --- a/xen/arch/x86/include/asm/processor.h
> +++ b/xen/arch/x86/include/asm/processor.h
> @@ -417,6 +417,9 @@ static inline uint8_t get_cpu_family(uin
>      return fam;
>  }
>  
> +void intel_process_freq(const struct cpuinfo_x86 *c,
> +                        unsigned int *min_mhz, unsigned int *max_mhz);
> +
>  #ifdef CONFIG_INTEL
>  extern int8_t opt_tsx;
>  extern bool rtm_disabled;
> --- a/xen/arch/x86/time.c
> +++ b/xen/arch/x86/time.c
> @@ -1664,6 +1664,9 @@ s_time_t get_s_time_fixed(uint64_t at_ts
>      const struct cpu_time *t = &this_cpu(cpu_time);
>      uint64_t tsc, delta;
>  
> +    /* scale_delta() degenerates when the scale wasn't set yet. */
> +    ASSERT(t->tsc_scale.mul_frac);
> +
>      if ( at_tsc )
>          tsc = at_tsc;
>      else
> @@ -1679,6 +1682,20 @@ s_time_t get_s_time_fixed(uint64_t at_ts
>  
>  s_time_t get_s_time(void)
>  {
> +    /*
> +     * Before the TSC scale is set, avoid returning constant 0 (or whatever
> +     * this_cpu(cpu_time).stamp.local_stime is set to).  While the returned
> +     * value is in no way representing time, it at least increases
> +     * monotonically, thus avoiding e.g. waiting loops to degenerate to
> +     * entirely infinite ones.
> +     */
> +    if ( unlikely(!this_cpu(cpu_time).tsc_scale.mul_frac) )
> +    {
> +        static s_time_t counter;

Not that it matters much, but counter can probably be __initdata?

Thanks, Roger.



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.