Skip to content

Commit 7560c02

Browse files
paulmckrcuKAGA-KOKO
authored andcommitted
clocksource: Check per-CPU clock synchronization when marked unstable
Some sorts of per-CPU clock sources have a history of going out of synchronization with each other. However, this problem has purportedy been solved in the past ten years. Except that it is all too possible that the problem has instead simply been made less likely, which might mean that some of the occasional "Marking clocksource 'tsc' as unstable" messages might be due to desynchronization. How would anyone know? Therefore apply CPU-to-CPU synchronization checking to newly unstable clocksource that are marked with the new CLOCK_SOURCE_VERIFY_PERCPU flag. Lists of desynchronized CPUs are printed, with the caveat that if it is the reporting CPU that is itself desynchronized, it will appear that all the other clocks are wrong. Just like in real life. Reported-by: Chris Mason <[email protected]> Signed-off-by: Paul E. McKenney <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Acked-by: Feng Tang <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent db3a34e commit 7560c02

File tree

3 files changed

+63
-2
lines changed

3 files changed

+63
-2
lines changed

arch/x86/kernel/tsc.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1152,7 +1152,8 @@ static struct clocksource clocksource_tsc = {
11521152
.mask = CLOCKSOURCE_MASK(64),
11531153
.flags = CLOCK_SOURCE_IS_CONTINUOUS |
11541154
CLOCK_SOURCE_VALID_FOR_HRES |
1155-
CLOCK_SOURCE_MUST_VERIFY,
1155+
CLOCK_SOURCE_MUST_VERIFY |
1156+
CLOCK_SOURCE_VERIFY_PERCPU,
11561157
.vdso_clock_mode = VDSO_CLOCKMODE_TSC,
11571158
.enable = tsc_cs_enable,
11581159
.resume = tsc_resume,

include/linux/clocksource.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ struct clocksource {
137137
#define CLOCK_SOURCE_UNSTABLE 0x40
138138
#define CLOCK_SOURCE_SUSPEND_NONSTOP 0x80
139139
#define CLOCK_SOURCE_RESELECT 0x100
140-
140+
#define CLOCK_SOURCE_VERIFY_PERCPU 0x200
141141
/* simplify initialization of mask field */
142142
#define CLOCKSOURCE_MASK(bits) GENMASK_ULL((bits) - 1, 0)
143143

kernel/time/clocksource.c

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,60 @@ static bool cs_watchdog_read(struct clocksource *cs, u64 *csnow, u64 *wdnow)
224224
return false;
225225
}
226226

227+
static u64 csnow_mid;
228+
static cpumask_t cpus_ahead;
229+
static cpumask_t cpus_behind;
230+
231+
static void clocksource_verify_one_cpu(void *csin)
232+
{
233+
struct clocksource *cs = (struct clocksource *)csin;
234+
235+
csnow_mid = cs->read(cs);
236+
}
237+
238+
static void clocksource_verify_percpu(struct clocksource *cs)
239+
{
240+
int64_t cs_nsec, cs_nsec_max = 0, cs_nsec_min = LLONG_MAX;
241+
u64 csnow_begin, csnow_end;
242+
int cpu, testcpu;
243+
s64 delta;
244+
245+
cpumask_clear(&cpus_ahead);
246+
cpumask_clear(&cpus_behind);
247+
preempt_disable();
248+
testcpu = smp_processor_id();
249+
pr_warn("Checking clocksource %s synchronization from CPU %d.\n", cs->name, testcpu);
250+
for_each_online_cpu(cpu) {
251+
if (cpu == testcpu)
252+
continue;
253+
csnow_begin = cs->read(cs);
254+
smp_call_function_single(cpu, clocksource_verify_one_cpu, cs, 1);
255+
csnow_end = cs->read(cs);
256+
delta = (s64)((csnow_mid - csnow_begin) & cs->mask);
257+
if (delta < 0)
258+
cpumask_set_cpu(cpu, &cpus_behind);
259+
delta = (csnow_end - csnow_mid) & cs->mask;
260+
if (delta < 0)
261+
cpumask_set_cpu(cpu, &cpus_ahead);
262+
delta = clocksource_delta(csnow_end, csnow_begin, cs->mask);
263+
cs_nsec = clocksource_cyc2ns(delta, cs->mult, cs->shift);
264+
if (cs_nsec > cs_nsec_max)
265+
cs_nsec_max = cs_nsec;
266+
if (cs_nsec < cs_nsec_min)
267+
cs_nsec_min = cs_nsec;
268+
}
269+
preempt_enable();
270+
if (!cpumask_empty(&cpus_ahead))
271+
pr_warn(" CPUs %*pbl ahead of CPU %d for clocksource %s.\n",
272+
cpumask_pr_args(&cpus_ahead), testcpu, cs->name);
273+
if (!cpumask_empty(&cpus_behind))
274+
pr_warn(" CPUs %*pbl behind CPU %d for clocksource %s.\n",
275+
cpumask_pr_args(&cpus_behind), testcpu, cs->name);
276+
if (!cpumask_empty(&cpus_ahead) || !cpumask_empty(&cpus_behind))
277+
pr_warn(" CPU %d check durations %lldns - %lldns for clocksource %s.\n",
278+
testcpu, cs_nsec_min, cs_nsec_max, cs->name);
279+
}
280+
227281
static void clocksource_watchdog(struct timer_list *unused)
228282
{
229283
u64 csnow, wdnow, cslast, wdlast, delta;
@@ -448,6 +502,12 @@ static int __clocksource_watchdog_kthread(void)
448502
unsigned long flags;
449503
int select = 0;
450504

505+
/* Do any required per-CPU skew verification. */
506+
if (curr_clocksource &&
507+
curr_clocksource->flags & CLOCK_SOURCE_UNSTABLE &&
508+
curr_clocksource->flags & CLOCK_SOURCE_VERIFY_PERCPU)
509+
clocksource_verify_percpu(curr_clocksource);
510+
451511
spin_lock_irqsave(&watchdog_lock, flags);
452512
list_for_each_entry_safe(cs, tmp, &watchdog_list, wd_list) {
453513
if (cs->flags & CLOCK_SOURCE_UNSTABLE) {

0 commit comments

Comments
 (0)