* [RFC][PATCH 0/4] sched_clock vs TSC wreckage
@ 2016-12-15 13:04 Peter Zijlstra
2016-12-15 13:04 ` [RFC][PATCH 1/4] clocksource: Add optional cs::mark_unstable() method Peter Zijlstra
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Peter Zijlstra @ 2016-12-15 13:04 UTC (permalink / raw)
To: tglx, mingo, rafael, nmorey
Cc: srinivas.pandruvada, jacob.jun.pan, linux-kernel, peterz
A few weeks ago Nicolas reported some impressive TSC wreckage on his i7-3820
running on an AsusTek Rampage IV Gene board [*].
These are a few patches that should hopefully allow us to do better in the face
of such 'creativity'.
[*] to be fair, Asus did in fact release a BIOS update that fixes this issue.
^ permalink raw reply [flat|nested] 5+ messages in thread
* [RFC][PATCH 1/4] clocksource: Add optional cs::mark_unstable() method
2016-12-15 13:04 [RFC][PATCH 0/4] sched_clock vs TSC wreckage Peter Zijlstra
@ 2016-12-15 13:04 ` Peter Zijlstra
2016-12-15 13:04 ` [RFC][PATCH 2/4] sched/clock: Update static_key usage Peter Zijlstra
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Peter Zijlstra @ 2016-12-15 13:04 UTC (permalink / raw)
To: tglx, mingo, rafael, nmorey
Cc: srinivas.pandruvada, jacob.jun.pan, linux-kernel, peterz
[-- Attachment #1: tglx-clocksource-unstable.patch --]
[-- Type: text/plain, Size: 2474 bytes --]
From: Thomas Gleixner <tglx@linutronix.de>
PeterZ reported that we'd fail to mark the TSC unstable when the
clocksource watchdog finds it unsuitable.
Allow a clocksource to run a custom action when its being marked
unstable and hook up the TSC unstable code.
Reported-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
arch/x86/kernel/tsc.c | 11 +++++++++++
include/linux/clocksource.h | 3 +++
kernel/time/clocksource.c | 4 ++++
3 files changed, 18 insertions(+)
--- a/arch/x86/kernel/tsc.c
+++ b/arch/x86/kernel/tsc.c
@@ -1101,6 +1101,16 @@ static cycle_t read_tsc(struct clocksour
return (cycle_t)rdtsc_ordered();
}
+static void tsc_cs_mark_unstable(struct clocksource *cs)
+{
+ if (tsc_unstable)
+ return;
+ tsc_unstable = 1;
+ clear_sched_clock_stable();
+ disable_sched_clock_irqtime();
+ pr_info("Marking TSC unstable due to clocksource watchdog\n");
+}
+
/*
* .mask MUST be CLOCKSOURCE_MASK(64). See comment above read_tsc()
*/
@@ -1112,6 +1122,7 @@ static struct clocksource clocksource_ts
.flags = CLOCK_SOURCE_IS_CONTINUOUS |
CLOCK_SOURCE_MUST_VERIFY,
.archdata = { .vclock_mode = VCLOCK_TSC },
+ .mark_unstable = tsc_cs_mark_unstable,
};
void mark_tsc_unstable(char *reason)
--- a/include/linux/clocksource.h
+++ b/include/linux/clocksource.h
@@ -62,6 +62,8 @@ struct module;
* @archdata: arch-specific data
* @suspend: suspend function for the clocksource, if necessary
* @resume: resume function for the clocksource, if necessary
+ * @mark_unstable: Optional function to inform the clocksource driver that
+ * the watchdog marked the clocksource unstable
* @owner: module reference, must be set by clocksource in modules
*
* Note: This struct is not used in hotpathes of the timekeeping code
@@ -93,6 +95,7 @@ struct clocksource {
unsigned long flags;
void (*suspend)(struct clocksource *cs);
void (*resume)(struct clocksource *cs);
+ void (*mark_unstable)(struct clocksource *cs);
/* private: */
#ifdef CONFIG_CLOCKSOURCE_WATCHDOG
--- a/kernel/time/clocksource.c
+++ b/kernel/time/clocksource.c
@@ -140,6 +140,10 @@ static void __clocksource_unstable(struc
{
cs->flags &= ~(CLOCK_SOURCE_VALID_FOR_HRES | CLOCK_SOURCE_WATCHDOG);
cs->flags |= CLOCK_SOURCE_UNSTABLE;
+
+ if (cs->mark_unstable)
+ cs->mark_unstable(cs);
+
if (finished_booting)
schedule_work(&watchdog_work);
}
^ permalink raw reply [flat|nested] 5+ messages in thread
* [RFC][PATCH 2/4] sched/clock: Update static_key usage
2016-12-15 13:04 [RFC][PATCH 0/4] sched_clock vs TSC wreckage Peter Zijlstra
2016-12-15 13:04 ` [RFC][PATCH 1/4] clocksource: Add optional cs::mark_unstable() method Peter Zijlstra
@ 2016-12-15 13:04 ` Peter Zijlstra
2016-12-15 13:04 ` [RFC][PATCH 3/4] sched/clock: Delay switching sched_clock to stable Peter Zijlstra
2016-12-15 13:04 ` [RFC][PATCH 4/4] sched/clock: Provide better clock continuity Peter Zijlstra
3 siblings, 0 replies; 5+ messages in thread
From: Peter Zijlstra @ 2016-12-15 13:04 UTC (permalink / raw)
To: tglx, mingo, rafael, nmorey
Cc: srinivas.pandruvada, jacob.jun.pan, linux-kernel, peterz
[-- Attachment #1: peterz-sched-clock-static_branch.patch --]
[-- Type: text/plain, Size: 1280 bytes --]
sched_clock was still using the deprecated static_key interface.
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
kernel/sched/clock.c | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
--- a/kernel/sched/clock.c
+++ b/kernel/sched/clock.c
@@ -78,19 +78,17 @@ EXPORT_SYMBOL_GPL(sched_clock);
__read_mostly int sched_clock_running;
#ifdef CONFIG_HAVE_UNSTABLE_SCHED_CLOCK
-static struct static_key __sched_clock_stable = STATIC_KEY_INIT;
+static DEFINE_STATIC_KEY_FALSE(__sched_clock_stable);
static int __sched_clock_stable_early;
int sched_clock_stable(void)
{
- return static_key_false(&__sched_clock_stable);
+ return static_branch_likely(&__sched_clock_stable);
}
static void __set_sched_clock_stable(void)
{
- if (!sched_clock_stable())
- static_key_slow_inc(&__sched_clock_stable);
-
+ static_branch_enable(&__sched_clock_stable);
tick_dep_clear(TICK_DEP_BIT_CLOCK_UNSTABLE);
}
@@ -109,9 +107,7 @@ void set_sched_clock_stable(void)
static void __clear_sched_clock_stable(struct work_struct *work)
{
/* XXX worry about clock continuity */
- if (sched_clock_stable())
- static_key_slow_dec(&__sched_clock_stable);
-
+ static_branch_disable(&__sched_clock_stable);
tick_dep_set(TICK_DEP_BIT_CLOCK_UNSTABLE);
}
^ permalink raw reply [flat|nested] 5+ messages in thread
* [RFC][PATCH 3/4] sched/clock: Delay switching sched_clock to stable
2016-12-15 13:04 [RFC][PATCH 0/4] sched_clock vs TSC wreckage Peter Zijlstra
2016-12-15 13:04 ` [RFC][PATCH 1/4] clocksource: Add optional cs::mark_unstable() method Peter Zijlstra
2016-12-15 13:04 ` [RFC][PATCH 2/4] sched/clock: Update static_key usage Peter Zijlstra
@ 2016-12-15 13:04 ` Peter Zijlstra
2016-12-15 13:04 ` [RFC][PATCH 4/4] sched/clock: Provide better clock continuity Peter Zijlstra
3 siblings, 0 replies; 5+ messages in thread
From: Peter Zijlstra @ 2016-12-15 13:04 UTC (permalink / raw)
To: tglx, mingo, rafael, nmorey
Cc: srinivas.pandruvada, jacob.jun.pan, linux-kernel, peterz
[-- Attachment #1: peterz-sched-clock-init_late.patch --]
[-- Type: text/plain, Size: 4363 bytes --]
Currently we switch to the stable sched_clock if we guess the TSC is
usable, and then switch back to the unstable path if it turns out TSC
isn't stable during SMP bringup after all.
Delay switching to the stable path until after SMP bringup is
complete. This way we'll avoid switching during the time we detect the
worst of the TSC offences.
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
include/linux/sched.h | 5 +++++
init/main.c | 1 -
kernel/sched/clock.c | 50 ++++++++++++++++++++++----------------------------
kernel/sched/core.c | 4 ++++
4 files changed, 31 insertions(+), 29 deletions(-)
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -2499,6 +2499,10 @@ extern u64 sched_clock_cpu(int cpu);
extern void sched_clock_init(void);
#ifndef CONFIG_HAVE_UNSTABLE_SCHED_CLOCK
+static inline void sched_clock_init_late(void)
+{
+}
+
static inline void sched_clock_tick(void)
{
}
@@ -2521,6 +2525,7 @@ static inline u64 local_clock(void)
return sched_clock();
}
#else
+extern void sched_clock_init_late(void);
/*
* Architectures can set this to 1 if they have specified
* CONFIG_HAVE_UNSTABLE_SCHED_CLOCK in their arch Kconfig,
--- a/init/main.c
+++ b/init/main.c
@@ -616,7 +616,6 @@ asmlinkage __visible void __init start_k
numa_policy_init();
if (late_time_init)
late_time_init();
- sched_clock_init();
calibrate_delay();
pidmap_init();
anon_vma_init();
--- a/kernel/sched/clock.c
+++ b/kernel/sched/clock.c
@@ -77,6 +77,11 @@ EXPORT_SYMBOL_GPL(sched_clock);
__read_mostly int sched_clock_running;
+void sched_clock_init(void)
+{
+ sched_clock_running = 1;
+}
+
#ifdef CONFIG_HAVE_UNSTABLE_SCHED_CLOCK
static DEFINE_STATIC_KEY_FALSE(__sched_clock_stable);
static int __sched_clock_stable_early;
@@ -96,12 +101,18 @@ void set_sched_clock_stable(void)
{
__sched_clock_stable_early = 1;
- smp_mb(); /* matches sched_clock_init() */
-
- if (!sched_clock_running)
- return;
+ smp_mb(); /* matches sched_clock_init_late() */
- __set_sched_clock_stable();
+ /*
+ * This really should only be called early (before
+ * sched_clock_init_late()) when guestimating our sched_clock() is
+ * solid.
+ *
+ * After that we test stability and we can negate our guess using
+ * clear_sched_clock_stable, possibly from a watchdog.
+ */
+ if (WARN_ON_ONCE(sched_clock_running == 2))
+ __set_sched_clock_stable();
}
static void __clear_sched_clock_stable(struct work_struct *work)
@@ -117,12 +128,10 @@ void clear_sched_clock_stable(void)
{
__sched_clock_stable_early = 0;
- smp_mb(); /* matches sched_clock_init() */
-
- if (!sched_clock_running)
- return;
+ smp_mb(); /* matches sched_clock_init_late() */
- schedule_work(&sched_clock_work);
+ if (sched_clock_running == 2)
+ schedule_work(&sched_clock_work);
}
struct sched_clock_data {
@@ -143,20 +152,9 @@ static inline struct sched_clock_data *c
return &per_cpu(sched_clock_data, cpu);
}
-void sched_clock_init(void)
+void sched_clock_init_late(void)
{
- u64 ktime_now = ktime_to_ns(ktime_get());
- int cpu;
-
- for_each_possible_cpu(cpu) {
- struct sched_clock_data *scd = cpu_sdc(cpu);
-
- scd->tick_raw = 0;
- scd->tick_gtod = ktime_now;
- scd->clock = ktime_now;
- }
-
- sched_clock_running = 1;
+ sched_clock_running = 2;
/*
* Ensure that it is impossible to not do a static_key update.
@@ -362,11 +360,6 @@ EXPORT_SYMBOL_GPL(sched_clock_idle_wakeu
#else /* CONFIG_HAVE_UNSTABLE_SCHED_CLOCK */
-void sched_clock_init(void)
-{
- sched_clock_running = 1;
-}
-
u64 sched_clock_cpu(int cpu)
{
if (unlikely(!sched_clock_running))
@@ -374,6 +367,7 @@ u64 sched_clock_cpu(int cpu)
return sched_clock();
}
+
#endif /* CONFIG_HAVE_UNSTABLE_SCHED_CLOCK */
/*
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -7486,6 +7486,7 @@ void __init sched_init_smp(void)
init_sched_dl_class();
sched_init_smt();
+ sched_clock_init_late();
sched_smp_initialized = true;
}
@@ -7501,6 +7502,7 @@ early_initcall(migration_init);
void __init sched_init_smp(void)
{
sched_init_granularity();
+ sched_clock_init_late();
}
#endif /* CONFIG_SMP */
@@ -7544,6 +7546,8 @@ void __init sched_init(void)
int i, j;
unsigned long alloc_size = 0, ptr;
+ sched_clock_init();
+
for (i = 0; i < WAIT_TABLE_SIZE; i++)
init_waitqueue_head(bit_wait_table + i);
^ permalink raw reply [flat|nested] 5+ messages in thread
* [RFC][PATCH 4/4] sched/clock: Provide better clock continuity
2016-12-15 13:04 [RFC][PATCH 0/4] sched_clock vs TSC wreckage Peter Zijlstra
` (2 preceding siblings ...)
2016-12-15 13:04 ` [RFC][PATCH 3/4] sched/clock: Delay switching sched_clock to stable Peter Zijlstra
@ 2016-12-15 13:04 ` Peter Zijlstra
3 siblings, 0 replies; 5+ messages in thread
From: Peter Zijlstra @ 2016-12-15 13:04 UTC (permalink / raw)
To: tglx, mingo, rafael, nmorey
Cc: srinivas.pandruvada, jacob.jun.pan, linux-kernel, peterz
[-- Attachment #1: peterz-sched-clock-continuity.patch --]
[-- Type: text/plain, Size: 5655 bytes --]
When switching between the unstable and stable variants it is
currently possible that clock discontinuities occur.
And while these will mostly be 'small', attempt to do better.
As observed on my IVB-EP, the sched_clock() is ~1.5s ahead of the
ktime_get_ns() based timeline at the point of switchover
(sched_clock_init_late()) after SMP bringup.
Equally, when the TSC is later found to be unstable -- typically
because SMM tries to hide its SMI latencies by mucking with the TSC --
we want to avoid large jumps.
Since the clocksource watchdog reports the issue after the fact we
cannot exactly fix up time, but since SMI latencies are typically
small (~10ns range), the discontinuity is mainly due to drift between
sched_clock() and ktime_get_ns() (which on my desktop is ~79s over
24days).
I dislike this patch because it adds overhead to the good case in
favour of dealing with badness. But given the widespread failure of
TSC stability this is worth it.
Note that in case the TSC makes drastic jumps after SMP bringup we're
still hosed. There's just not much we can do in that case without
stupid overhead.
If we were to somehow expose tsc_clocksource_reliable (which is hard
because this code is also used on ia64 and parisc) we could avoid some
of the newly introduced overhead.
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
kernel/sched/clock.c | 99 +++++++++++++++++++++++++++++++++------------------
1 file changed, 65 insertions(+), 34 deletions(-)
--- a/kernel/sched/clock.c
+++ b/kernel/sched/clock.c
@@ -86,6 +86,30 @@ void sched_clock_init(void)
static DEFINE_STATIC_KEY_FALSE(__sched_clock_stable);
static int __sched_clock_stable_early;
+/*
+ * We want: ktime_get_ns() + gtod_offset == sched_clock() + raw_offset
+ */
+static __read_mostly u64 raw_offset;
+static __read_mostly u64 gtod_offset;
+
+struct sched_clock_data {
+ u64 tick_raw;
+ u64 tick_gtod;
+ u64 clock;
+};
+
+static DEFINE_PER_CPU_SHARED_ALIGNED(struct sched_clock_data, sched_clock_data);
+
+static inline struct sched_clock_data *this_scd(void)
+{
+ return this_cpu_ptr(&sched_clock_data);
+}
+
+static inline struct sched_clock_data *cpu_sdc(int cpu)
+{
+ return &per_cpu(sched_clock_data, cpu);
+}
+
int sched_clock_stable(void)
{
return static_branch_likely(&__sched_clock_stable);
@@ -93,6 +117,17 @@ int sched_clock_stable(void)
static void __set_sched_clock_stable(void)
{
+ struct sched_clock_data *scd = this_scd();
+
+ /*
+ * Attempt to make the (initial) unstable->stable transition continuous.
+ */
+ raw_offset = (scd->tick_gtod + gtod_offset) - (scd->tick_raw);
+
+ printk(KERN_INFO "sched_clock: Marking stable (%lld, %lld)->(%lld, %lld)\n",
+ scd->tick_gtod, gtod_offset,
+ scd->tick_raw, raw_offset);
+
static_branch_enable(&__sched_clock_stable);
tick_dep_clear(TICK_DEP_BIT_CLOCK_UNSTABLE);
}
@@ -117,7 +152,23 @@ void set_sched_clock_stable(void)
static void __clear_sched_clock_stable(struct work_struct *work)
{
- /* XXX worry about clock continuity */
+ struct sched_clock_data *scd = this_scd();
+
+ /*
+ * Attempt to make the stable->unstable transition continuous.
+ *
+ * Trouble is, this is typically called from the TSC watchdog
+ * timer, which is late per definition. This means the tick
+ * values can already be screwy.
+ *
+ * Still do what we can.
+ */
+ gtod_offset = (scd->tick_raw + raw_offset) - (scd->tick_gtod);
+
+ printk(KERN_INFO "sched_clock: Marking unstable (%lld, %lld)<-(%lld, %lld)\n",
+ scd->tick_gtod, gtod_offset,
+ scd->tick_raw, raw_offset);
+
static_branch_disable(&__sched_clock_stable);
tick_dep_set(TICK_DEP_BIT_CLOCK_UNSTABLE);
}
@@ -134,28 +185,9 @@ void clear_sched_clock_stable(void)
schedule_work(&sched_clock_work);
}
-struct sched_clock_data {
- u64 tick_raw;
- u64 tick_gtod;
- u64 clock;
-};
-
-static DEFINE_PER_CPU_SHARED_ALIGNED(struct sched_clock_data, sched_clock_data);
-
-static inline struct sched_clock_data *this_scd(void)
-{
- return this_cpu_ptr(&sched_clock_data);
-}
-
-static inline struct sched_clock_data *cpu_sdc(int cpu)
-{
- return &per_cpu(sched_clock_data, cpu);
-}
-
void sched_clock_init_late(void)
{
sched_clock_running = 2;
-
/*
* Ensure that it is impossible to not do a static_key update.
*
@@ -210,7 +242,7 @@ static u64 sched_clock_local(struct sche
* scd->tick_gtod + TICK_NSEC);
*/
- clock = scd->tick_gtod + delta;
+ clock = scd->tick_gtod + gtod_offset + delta;
min_clock = wrap_max(scd->tick_gtod, old_clock);
max_clock = wrap_max(old_clock, scd->tick_gtod + TICK_NSEC);
@@ -296,7 +328,7 @@ u64 sched_clock_cpu(int cpu)
u64 clock;
if (sched_clock_stable())
- return sched_clock();
+ return sched_clock() + raw_offset;
if (unlikely(!sched_clock_running))
return 0ull;
@@ -317,23 +349,22 @@ EXPORT_SYMBOL_GPL(sched_clock_cpu);
void sched_clock_tick(void)
{
struct sched_clock_data *scd;
- u64 now, now_gtod;
-
- if (sched_clock_stable())
- return;
-
- if (unlikely(!sched_clock_running))
- return;
WARN_ON_ONCE(!irqs_disabled());
+ /*
+ * Update these values even if sched_clock_stable(), because it can
+ * become unstable at any point in time at which point we need some
+ * values to fall back on.
+ *
+ * XXX arguably we can skip this if we expose tsc_clocksource_reliable
+ */
scd = this_scd();
- now_gtod = ktime_to_ns(ktime_get());
- now = sched_clock();
+ scd->tick_raw = sched_clock();
+ scd->tick_gtod = ktime_get_ns();
- scd->tick_raw = now;
- scd->tick_gtod = now_gtod;
- sched_clock_local(scd);
+ if (!sched_clock_stable() && likely(sched_clock_running))
+ sched_clock_local(scd);
}
/*
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2016-12-15 13:13 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-12-15 13:04 [RFC][PATCH 0/4] sched_clock vs TSC wreckage Peter Zijlstra
2016-12-15 13:04 ` [RFC][PATCH 1/4] clocksource: Add optional cs::mark_unstable() method Peter Zijlstra
2016-12-15 13:04 ` [RFC][PATCH 2/4] sched/clock: Update static_key usage Peter Zijlstra
2016-12-15 13:04 ` [RFC][PATCH 3/4] sched/clock: Delay switching sched_clock to stable Peter Zijlstra
2016-12-15 13:04 ` [RFC][PATCH 4/4] sched/clock: Provide better clock continuity Peter Zijlstra
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Powered by JetHome