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
1313
1414 #ifdef CONFIG_LOCKUP_DETECTOR
1515 void lockup_detector_init(void);
16+void lockup_detector_retry_init(void);
1617 void lockup_detector_soft_poweroff(void);
1718 void lockup_detector_cleanup(void);
1819
@@ -32,6 +33,7 @@ extern int sysctl_hardlockup_all_cpu_backtrace; · 13 unmodified lines
3233
3334 #else /* CONFIG_LOCKUP_DETECTOR */
3435 static inline void lockup_detector_init(void) { }
36+static inline void lockup_detector_retry_init(void) { }
3537 static inline void lockup_detector_soft_poweroff(void) { }
3638 static inline void lockup_detector_cleanup(void) { }
3739 #endif /* !CONFIG_LOCKUP_DETECTOR */
kernel/watchdog.c+66−1
@@ -208,7 +208,13 @@ void __weak watchdog_hardlockup_enable(unsigned int cpu) { } · 207 unmodified lines
208208
209209 void __weak watchdog_hardlockup_disable(unsigned int cpu) { }
210210
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+ */
212218 int __weak __init watchdog_hardlockup_probe(void)
213219 {
214220 /*
@@ -954,6 +960,62 @@ static void __init watchdog_sysctl_init(void) · 739 unmodified lines
954960 #define watchdog_sysctl_init() do { } while (0)
955961 #endif /* CONFIG_SYSCTL */
956962
963+static void __init lockup_detector_delay_init(struct work_struct *work);
964+static bool allow_lockup_detector_init_retry __initdata;
965+
966+static struct work_struct detector_work __initdata =
967+ __WORK_INITIALIZER(detector_work, lockup_detector_delay_init);
968+
969+static 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+ */
993+void __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+ */
1006+static 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+}
1017+late_initcall_sync(lockup_detector_check);
1018+
9571019 void __init lockup_detector_init(void)
9581020 {
9591021 if (tick_nohz_full_enabled())
@@ -964,6 +1026,9 @@ void __init lockup_detector_init(void) · 4 unmodified lines
9641026
9651027 if (!watchdog_hardlockup_probe())
9661028 watchdog_hardlockup_available = true;
1029+ else
1030+ allow_lockup_detector_init_retry = true;
1031+
9671032 lockup_detector_setup();
9681033 watchdog_sysctl_init();
9691034 }