[PATCH 2/2] arm64/watchdog_hld: Add a cpufreq notifier for update watchdog thresh

Yicong Yang yangyicong at huawei.com
Tue Mar 11 01:32:53 PDT 2025


On 2025/3/8 0:53, Doug Anderson wrote:
> Hi,
> 
> On Thu, Mar 6, 2025 at 6:18 PM Yicong Yang <yangyicong at huawei.com> wrote:
>>
>> From: Yicong Yang <yangyicong at hisilicon.com>
>>
>> arm64 depends on the cpufreq driver to gain the maximum cpu frequency
>> to convert the watchdog_thresh to perf event period. cpufreq drivers
>> like cppc_cpufreq will be initialized lately after the initializing of
>> the hard lockup detector so just use a safe cpufreq which will be
>> inaccurency. Use a cpufreq notifier to adjust the event's period to
>> a more accurate one.
>>
>> Signed-off-by: Yicong Yang <yangyicong at hisilicon.com>
>> ---
>>  arch/arm64/kernel/watchdog_hld.c | 34 ++++++++++++++++++++++++++++++++
>>  1 file changed, 34 insertions(+)
>>
>> diff --git a/arch/arm64/kernel/watchdog_hld.c b/arch/arm64/kernel/watchdog_hld.c
>> index dcd25322127c..719ae39e8bc0 100644
>> --- a/arch/arm64/kernel/watchdog_hld.c
>> +++ b/arch/arm64/kernel/watchdog_hld.c
>> @@ -34,3 +34,37 @@ bool __init arch_perf_nmi_is_available(void)
>>          */
>>         return arm_pmu_irq_is_nmi();
>>  }
>> +
>> +static int watchdog_freq_notifier_callback(struct notifier_block *nb,
>> +                                          unsigned long val, void *data)
>> +{
>> +       struct cpufreq_policy *policy = data;
>> +       unsigned long max_cpu_freq;
> 
> Feels like max_cpu_freq should be a u64. It probably should have been
> u64 in hw_nmi_get_sample_period() as well. Otherwise you could end up
> with a problem if the max CPU frequency is above ~4.3GHz and "unsigned
> long" is 32-bits (I think it technically could be?)
> 

I guess this won't happen for arm64 and we're in kernel space. here I
keep the same type with hw_nmi_get_sample_period() which is also using
"unsigned long".

> 
>> +       u64 new_period;
>> +       int cpu;
>> +
>> +       if (val != CPUFREQ_CREATE_POLICY)
>> +               return 0;
> 
> nit: maybe better documenting to return "NOTIFY_DONE" instead of 0?
> 

will use NOTIFY_NONE.

> 
>> +       for_each_cpu(cpu, policy->cpus) {
>> +               max_cpu_freq = cpufreq_get_hw_max_freq(cpu) * 1000UL;
>> +               if (!max_cpu_freq)
>> +                       continue;
>> +
>> +               new_period = watchdog_thresh * max_cpu_freq;
>> +               hardlockup_detector_perf_adjust_period(cpu, new_period);
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static struct notifier_block watchdog_freq_notifier = {
>> +       .notifier_call = watchdog_freq_notifier_callback,
>> +};
>> +
>> +static int __init init_watchdog_freq_notifier(void)
>> +{
>> +       return cpufreq_register_notifier(&watchdog_freq_notifier,
>> +                                        CPUFREQ_POLICY_NOTIFIER);
>> +}
>> +core_initcall(init_watchdog_freq_notifier);
> 
> I haven't dug into everything, but don't you have a potential blind
> spot, though? What if the order is this:
> 
> 1. hw_nmi_get_sample_period() is called
> 2. cpufreq driver probes
> 3. init_watchdog_freq_notifier() initcall runs
> 
> Since the cpufreq driver proved before you registered for the
> notifier, won't you miss the creation of the policy? Maybe instead of
> registering for an initcall you should somehow trigger off the first
> call to hw_nmi_get_sample_period() and register at that point in time?
> 

for arm64 the watchdog_perf is registered after armv8 PMU driver's init,
so the sequence is like:

# core_initcall
init_watchdog_freq_notifier()
# device_initcall
armv8_pmu_driver_init()
  lockup_detector_retry_init()
    schedule_work(&detector_work);
# in detector work
hw_nmi_get_sample_period() // notifier's already been registered

so we'll always get the correct frequency, either by the cpufreq
already registered or by the notifier which will update the
frequency. Please correct me if I missed anything :)

Thanks.





More information about the linux-arm-kernel mailing list