From 148939a4708464d273746558649f224afe88eaef Mon Sep 17 00:00:00 2001 From: Daniel Byrne Date: Tue, 27 Oct 2026 05:57:08 -0700 Subject: [PATCH] Fix SIGFPE at startup when CPUID leaf 0x15 is unusable with tpause With DTO_WAIT_METHOD=tpause, init_dto() converts the wait from ns to TSC cycles using CPUID leaf 0x15 and divides by the denominator unconditionally. If the leaf is absent (__get_cpuid() returns 0 with the outputs uninitialized) or reports den=0, as on many VMs, the constructor dies with SIGFPE before main(). It also ran after dsa_init() had failed and the library was falling back to CPU, where the value is unused. Only do the conversion when DSA is in use and tpause is selected, and require the leaf to be present with non-zero den, num and frequency. Otherwise log an error and fall back to busypoll. Also fix waitpkg detection in dsa_init(), which queried CPUID leaf 0 instead of leaf 7. Leaf 0's ECX is the ASCII "ntel", which has bit 5 set, so waitpkg_support was always 1 and tpause/umwait on a CPU without WAITPKG would SIGILL instead of falling back. --- dto.c | 63 ++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/dto.c b/dto.c index 5eb211e..cc43678 100644 --- a/dto.c +++ b/dto.c @@ -1255,10 +1255,10 @@ static int dsa_init(void) const char *env_str; char wq_list[256]; - /* detect waitpkg support */ - leaf = 7; + /* detect waitpkg support: CPUID.(EAX=7,ECX=0):ECX bit 5 */ + leaf = 0; waitpkg = 0; - if (__get_cpuid(0, &leaf, unused, &waitpkg, unused + 1)) { + if (__get_cpuid_count(7, 0, &leaf, unused, &waitpkg, unused + 1)) { if (waitpkg & 0x20) { LOG_TRACE("waitpkg supported\n"); waitpkg_support = 1; @@ -1539,27 +1539,42 @@ static int init_dto(void) use_std_lib_calls = 1; } - // calculate the wait time for TPAUSE - if (wait_method == WAIT_TPAUSE) { - unsigned int num, den, freq; - unsigned int empty; - unsigned long long tmp; - __get_cpuid( 0x15, &den, &num, &freq, &empty ); - freq /= 1000; - LOG_TRACE( "Core Freq = %u kHz\n", freq ); - LOG_TRACE( "TSC Mult = %u\n", num ); - LOG_TRACE( "TSC Den = %u\n", den ); - freq *= num; - freq /= den; - LOG_TRACE( "CPU freq = %u kHz\n", freq ); - - LOG_TRACE( "Requested wait: %llu nsec\n", tpause_wait_time ); - tmp = tpause_wait_time; - tmp *= freq; - tpause_wait_time = tmp / NSEC_PER_MSEC; - LOG_TRACE( "Requested wait duration: %llu cycles\n", tpause_wait_time ); - } - + /* + * Calculate the wait time for TPAUSE. This converts ns to TSC + * cycles using CPUID leaf 0x15, which may be absent or report a + * zero denominator/crystal frequency (common in VMs and on + * older parts), so verify it before dividing. + */ + if (!use_std_lib_calls && wait_method == WAIT_TPAUSE) { + unsigned int num = 0, den = 0, freq = 0; + unsigned int empty; + unsigned long long tmp; + + if (!__get_cpuid(0x15, &den, &num, &freq, &empty) || + den == 0 || num == 0 || freq == 0) { + LOG_ERROR("CPUID leaf 0x15 unavailable (den=%u, num=%u, freq=%u); " + "cannot compute TSC frequency for tpause. Falling back to busypoll\n", + den, num, freq); + wait_method = WAIT_BUSYPOLL; + min_avg_waits = MIN_AVG_POLL_WAITS; + max_avg_waits = MAX_AVG_POLL_WAITS; + } else { + freq /= 1000; + LOG_TRACE("Core Freq = %u kHz\n", freq); + LOG_TRACE("TSC Mult = %u\n", num); + LOG_TRACE("TSC Den = %u\n", den); + freq *= num; + freq /= den; + LOG_TRACE("CPU freq = %u kHz\n", freq); + + LOG_TRACE("Requested wait: %llu nsec\n", tpause_wait_time); + tmp = tpause_wait_time; + tmp *= freq; + tpause_wait_time = tmp / NSEC_PER_MSEC; + LOG_TRACE("Requested wait duration: %llu cycles\n", tpause_wait_time); + } + } + // display configuration LOG_TRACE("log_level: %d, collect_stats: %d, use_std_lib_calls: %d, dsa_min_size: %lu, " "cpu_size_fraction: %.2f, wait_method: %s, auto_adjust_knobs: %d, numa_awareness: %s, dto_dsa_cc: %d\n",