[PATCH] x86/tsc: avoid system instability in hibernation System
From: Atharva Tiwari
Date: Mon Dec 16 2024 - 05:38:48 EST
instability are seen during resume from hibernation when system is under heavy CPU load. this is caused by the lack of update of sched clock data
Signed-off-by: Atharva Tiwari <evepolonium@xxxxxxxxx>
---
arch/x86/kernel/tsc.c | 27 +++++++++++++++++++++++++++
include/linux/sched/clock.h | 5 +++++
kernel/sched/clock.c | 4 ++--
3 files changed, 34 insertions(+), 2 deletions(-)
diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
index 67aeaba4ba9c..1879ae5b49c8 100644
--- a/arch/x86/kernel/tsc.c
+++ b/arch/x86/kernel/tsc.c
@@ -15,6 +15,7 @@
#include <linux/timex.h>
#include <linux/static_key.h>
#include <linux/static_call.h>
+#include <linux/suspend.h>
#include <asm/hpet.h>
#include <asm/timer.h>
@@ -1599,3 +1600,29 @@ unsigned long calibrate_delay_is_known(void)
return 0;
}
#endif
+static int tsc_pm_notifier(struct notifier_block *notifier,
+ unsigned long pm_event, void *unused)
+{
+ switch (pm_event) {
+ case PM_HIBERNATION_PREPARE:
+ clear_sched_clock_stable();
+ break;
+ case PM_POST_HIBERNATION:
+ /* Set back to the default */
+ if (!check_tsc_unstable())
+ set_sched_clock_stable();
+ break;
+ }
+ return 0;
+};
+
+static struct notifier_block tsc_pm_notifier_block = {
+ .notifier_call = tsc_pm_notifier,
+};
+
+static int tsc_setup_pm_notifier(void)
+{
+ return register_pm_notifier(&tsc_pm_notifier_block);
+}
+
+subsys_initcall(tsc_setup_pm_notifier);
diff --git a/include/linux/sched/clock.h b/include/linux/sched/clock.h
index 196f0ca351a2..811b8ebb57a5 100644
--- a/include/linux/sched/clock.h
+++ b/include/linux/sched/clock.h
@@ -41,6 +41,10 @@ static inline void clear_sched_clock_stable(void)
{
}
+static inline void set_sched_clock_stable(void)
+{
+}
+
static inline void sched_clock_idle_sleep_event(void)
{
}
@@ -65,6 +69,7 @@ static __always_inline u64 local_clock(void)
}
#else
extern int sched_clock_stable(void);
+extern void set_sched_clock_stable(void);
extern void clear_sched_clock_stable(void);
/*
diff --git a/kernel/sched/clock.c b/kernel/sched/clock.c
index a09655b48140..efe8f2b69657 100644
--- a/kernel/sched/clock.c
+++ b/kernel/sched/clock.c
@@ -114,7 +114,7 @@ notrace static void __scd_stamp(struct sched_clock_data *scd)
scd->tick_raw = sched_clock();
}
-notrace static void __set_sched_clock_stable(void)
+notrace void set_sched_clock_stable(void)
{
struct sched_clock_data *scd;
@@ -234,7 +234,7 @@ static int __init sched_clock_init_late(void)
smp_mb(); /* matches {set,clear}_sched_clock_stable() */
if (__sched_clock_stable_early)
- __set_sched_clock_stable();
+ set_sched_clock_stable();
return 0;
}
--
2.43.0