History

watchdog/perf: adapt the watchdog_perf interface for async model

When lockup_detector_init()->watchdog_hardlockup_probe(), PMU may be not ready yet. E.g. on arm64, PMU is not ready until device_initcall(armv8_pmu_driver_init). And it is deeply integrated with the driver model and cpuhp. Hence it is hard to push this initialization before smp_init(). But it is easy to take an opposite approach and try to initialize the watchdog once again later. The delayed probe is called using workqueues. It need to allocate memory and must be proceed in a normal context. The delayed probe is able to use if watchdog_hardlockup_probe() returns non-zero which means the return code returned when PMU is not ready yet. Provide an API - lockup_detector_retry_init() for anyone who needs to delayed init lockup detector if they had ever failed at lockup_detector_init(). The original assumption is: nobody should use delayed probe after lockup_detector_check() which has __init attribute. That is, anyone uses this API must call between lockup_detector_init() and lockup_detector_check(), and the caller must have __init attribute

Lecopzer Chen committed 3 years ago

Commit 930d8f81 parent b17aa952 files · +68 −1Browse files

Unified · Split

2 files
  1. include/linux/nmi.h +2 −0
  2. kernel/watchdog.c +66 −1
include/linux/nmi.h+2−0
@@ -13,6 +13,7 @@ · 12 unmodified lines
13 13
14#ifdef CONFIG_LOCKUP_DETECTOR14#ifdef CONFIG_LOCKUP_DETECTOR
15void lockup_detector_init(void);15void lockup_detector_init(void);
16void lockup_detector_retry_init(void);
16void lockup_detector_soft_poweroff(void);17void lockup_detector_soft_poweroff(void);
17void lockup_detector_cleanup(void);18void lockup_detector_cleanup(void);
18 19
@@ -32,6 +33,7 @@ extern int sysctl_hardlockup_all_cpu_backtrace; · 13 unmodified lines
32 33
33#else /* CONFIG_LOCKUP_DETECTOR */34#else /* CONFIG_LOCKUP_DETECTOR */
34static inline void lockup_detector_init(void) { }35static inline void lockup_detector_init(void) { }
36static inline void lockup_detector_retry_init(void) { }
35static inline void lockup_detector_soft_poweroff(void) { }37static inline void lockup_detector_soft_poweroff(void) { }
36static inline void lockup_detector_cleanup(void) { }38static inline void lockup_detector_cleanup(void) { }
37#endif /* !CONFIG_LOCKUP_DETECTOR */39#endif /* !CONFIG_LOCKUP_DETECTOR */
kernel/watchdog.c+66−1
@@ -208,7 +208,13 @@ void __weak watchdog_hardlockup_enable(unsigned int cpu) { } · 207 unmodified lines
208 208
209void __weak watchdog_hardlockup_disable(unsigned int cpu) { }209void __weak watchdog_hardlockup_disable(unsigned int cpu) { }
210 210
211/* Return 0, if a hardlockup watchdog is available. Error code otherwise */211/*
212 * Watchdog-detector specific API.
213 *
214 * Return 0 when hardlockup watchdog is available, negative value otherwise.
215 * Note that the negative value means that a delayed probe might
216 * succeed later.
217 */
212int __weak __init watchdog_hardlockup_probe(void)218int __weak __init watchdog_hardlockup_probe(void)
213{219{
214 /*220 /*
@@ -954,6 +960,62 @@ static void __init watchdog_sysctl_init(void) · 739 unmodified lines
954#define watchdog_sysctl_init() do { } while (0)960#define watchdog_sysctl_init() do { } while (0)
955#endif /* CONFIG_SYSCTL */961#endif /* CONFIG_SYSCTL */
956 962
963static void __init lockup_detector_delay_init(struct work_struct *work);
964static bool allow_lockup_detector_init_retry __initdata;
965
966static struct work_struct detector_work __initdata =
967 __WORK_INITIALIZER(detector_work, lockup_detector_delay_init);
968
969static void __init lockup_detector_delay_init(struct work_struct *work)
970{
971 int ret;
972
973 ret = watchdog_hardlockup_probe();
974 if (ret) {
975 pr_info("Delayed init of the lockup detector failed: %d\n", ret);
976 pr_info("Hard watchdog permanently disabled\n");
977 return;
978 }
979
980 allow_lockup_detector_init_retry = false;
981
982 watchdog_hardlockup_available = true;
983 lockup_detector_setup();
984}
985
986/*
987 * lockup_detector_retry_init - retry init lockup detector if possible.
988 *
989 * Retry hardlockup detector init. It is useful when it requires some
990 * functionality that has to be initialized later on a particular
991 * platform.
992 */
993void __init lockup_detector_retry_init(void)
994{
995 /* Must be called before late init calls */
996 if (!allow_lockup_detector_init_retry)
997 return;
998
999 schedule_work(&detector_work);
1000}
1001
1002/*
1003 * Ensure that optional delayed hardlockup init is proceed before
1004 * the init code and memory is freed.
1005 */
1006static int __init lockup_detector_check(void)
1007{
1008 /* Prevent any later retry. */
1009 allow_lockup_detector_init_retry = false;
1010
1011 /* Make sure no work is pending. */
1012 flush_work(&detector_work);
1013
1014 return 0;
1015
1016}
1017late_initcall_sync(lockup_detector_check);
1018
957void __init lockup_detector_init(void)1019void __init lockup_detector_init(void)
958{1020{
959 if (tick_nohz_full_enabled())1021 if (tick_nohz_full_enabled())
@@ -964,6 +1026,9 @@ void __init lockup_detector_init(void) · 4 unmodified lines
964 1026
965 if (!watchdog_hardlockup_probe())1027 if (!watchdog_hardlockup_probe())
966 watchdog_hardlockup_available = true;1028 watchdog_hardlockup_available = true;
1029 else
1030 allow_lockup_detector_init_retry = true;
1031
967 lockup_detector_setup();1032 lockup_detector_setup();
968 watchdog_sysctl_init();1033 watchdog_sysctl_init();
969}1034}