Commit 75c7b6f3 authored by Martin Schwidefsky's avatar Martin Schwidefsky

s390/time: steer clocksource on STP sync events

On STP sync events the TOD clock will jump in time, either forward or
backward. The TOD clocksource claims to be continuous but in case of
an STP sync with a negative offset it is not.

Subtract the offset injected by the STP sync check from the result of
the TOD clocksource to make it continuous again. Add code to drift the
offset towards zero with a fixed rate, steering 1 second in ~9 hours.
Suggested-by: default avatarDavid Hildenbrand <dahi@linux.vnet.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 2ace06ec
...@@ -33,6 +33,8 @@ struct vdso_data { ...@@ -33,6 +33,8 @@ struct vdso_data {
__u32 ectg_available; /* ECTG instruction present 0x58 */ __u32 ectg_available; /* ECTG instruction present 0x58 */
__u32 tk_mult; /* Mult. used for xtime_nsec 0x5c */ __u32 tk_mult; /* Mult. used for xtime_nsec 0x5c */
__u32 tk_shift; /* Shift used for xtime_nsec 0x60 */ __u32 tk_shift; /* Shift used for xtime_nsec 0x60 */
__u32 ts_dir; /* TOD steering direction 0x64 */
__u64 ts_end; /* TOD steering end 0x68 */
}; };
struct vdso_per_cpu_data { struct vdso_per_cpu_data {
......
...@@ -79,6 +79,8 @@ int main(void) ...@@ -79,6 +79,8 @@ int main(void)
OFFSET(__VDSO_ECTG_OK, vdso_data, ectg_available); OFFSET(__VDSO_ECTG_OK, vdso_data, ectg_available);
OFFSET(__VDSO_TK_MULT, vdso_data, tk_mult); OFFSET(__VDSO_TK_MULT, vdso_data, tk_mult);
OFFSET(__VDSO_TK_SHIFT, vdso_data, tk_shift); OFFSET(__VDSO_TK_SHIFT, vdso_data, tk_shift);
OFFSET(__VDSO_TS_DIR, vdso_data, ts_dir);
OFFSET(__VDSO_TS_END, vdso_data, ts_end);
OFFSET(__VDSO_ECTG_BASE, vdso_per_cpu_data, ectg_timer_base); OFFSET(__VDSO_ECTG_BASE, vdso_per_cpu_data, ectg_timer_base);
OFFSET(__VDSO_ECTG_USER, vdso_per_cpu_data, ectg_user_time); OFFSET(__VDSO_ECTG_USER, vdso_per_cpu_data, ectg_user_time);
OFFSET(__VDSO_CPU_NR, vdso_per_cpu_data, cpu_nr); OFFSET(__VDSO_CPU_NR, vdso_per_cpu_data, cpu_nr);
......
...@@ -62,6 +62,8 @@ unsigned char ptff_function_mask[16]; ...@@ -62,6 +62,8 @@ unsigned char ptff_function_mask[16];
static unsigned long long lpar_offset; static unsigned long long lpar_offset;
static unsigned long long initial_leap_seconds; static unsigned long long initial_leap_seconds;
static unsigned long long tod_steering_end;
static unsigned long long tod_steering_delta;
/* /*
* Get time offsets with PTFF * Get time offsets with PTFF
...@@ -71,8 +73,13 @@ void __init time_early_init(void) ...@@ -71,8 +73,13 @@ void __init time_early_init(void)
struct ptff_qto qto; struct ptff_qto qto;
struct ptff_qui qui; struct ptff_qui qui;
/* Initialize TOD steering parameters */
tod_steering_end = sched_clock_base_cc;
vdso_data->ts_end = tod_steering_end;
if (!test_facility(28)) if (!test_facility(28))
return; return;
ptff(&ptff_function_mask, sizeof(ptff_function_mask), PTFF_QAF); ptff(&ptff_function_mask, sizeof(ptff_function_mask), PTFF_QAF);
/* get LPAR offset */ /* get LPAR offset */
...@@ -204,7 +211,22 @@ void read_boot_clock64(struct timespec64 *ts) ...@@ -204,7 +211,22 @@ void read_boot_clock64(struct timespec64 *ts)
static cycle_t read_tod_clock(struct clocksource *cs) static cycle_t read_tod_clock(struct clocksource *cs)
{ {
return get_tod_clock(); unsigned long long now, adj;
preempt_disable(); /* protect from changes to steering parameters */
now = get_tod_clock();
adj = tod_steering_end - now;
if (unlikely((s64) adj >= 0))
/*
* manually steer by 1 cycle every 2^16 cycles. This
* corresponds to shifting the tod delta by 15. 1s is
* therefore steered in ~9h. The adjust will decrease
* over time, until it finally reaches 0.
*/
now += ((s64) tod_steering_delta < 0) ?
(adj >> 15) : -(adj >> 15);
preempt_enable();
return now;
} }
static struct clocksource clocksource_tod = { static struct clocksource clocksource_tod = {
...@@ -379,10 +401,27 @@ static inline int check_sync_clock(void) ...@@ -379,10 +401,27 @@ static inline int check_sync_clock(void)
*/ */
static void clock_sync_global(unsigned long long delta) static void clock_sync_global(unsigned long long delta)
{ {
unsigned long now, adj;
struct ptff_qto qto; struct ptff_qto qto;
/* Fixup the monotonic sched clock. */ /* Fixup the monotonic sched clock. */
sched_clock_base_cc += delta; sched_clock_base_cc += delta;
/* Adjust TOD steering parameters. */
vdso_data->tb_update_count++;
now = get_tod_clock();
adj = tod_steering_end - now;
if (unlikely((s64) adj >= 0))
/* Calculate how much of the old adjustment is left. */
tod_steering_delta = ((s64) tod_steering_delta < 0) ?
-(adj >> 15) : (adj >> 15);
tod_steering_delta += delta;
if ((abs(tod_steering_delta) >> 48) != 0)
panic("TOD clock sync offset %lli is too large to drift\n",
tod_steering_delta);
tod_steering_end = now + (abs(tod_steering_delta) << 15);
vdso_data->ts_dir = (tod_steering_delta < 0) ? 0 : 1;
vdso_data->ts_end = tod_steering_end;
vdso_data->tb_update_count++;
/* Update LPAR offset. */ /* Update LPAR offset. */
if (ptff_query(PTFF_QTO) && ptff(&qto, sizeof(qto), PTFF_QTO) == 0) if (ptff_query(PTFF_QTO) && ptff(&qto, sizeof(qto), PTFF_QTO) == 0)
lpar_offset = qto.tod_epoch_difference; lpar_offset = qto.tod_epoch_difference;
......
...@@ -99,8 +99,27 @@ __kernel_clock_gettime: ...@@ -99,8 +99,27 @@ __kernel_clock_gettime:
tml %r4,0x0001 /* pending update ? loop */ tml %r4,0x0001 /* pending update ? loop */
jnz 11b jnz 11b
stcke 0(%r15) /* Store TOD clock */ stcke 0(%r15) /* Store TOD clock */
lm %r0,%r1,1(%r15) lm %r0,%r1,__VDSO_TS_END(%r5) /* TOD steering end time */
s %r0,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */ s %r0,1(%r15) /* no - ts_steering_end */
sl %r1,5(%r15)
brc 3,22f
ahi %r0,-1
22: ltr %r0,%r0 /* past end of steering? */
jm 24f
srdl %r0,15 /* 1 per 2^16 */
tm __VDSO_TS_DIR+3(%r5),0x01 /* steering direction? */
jz 23f
lcr %r0,%r0 /* negative TOD offset */
lcr %r1,%r1
je 23f
ahi %r0,-1
23: a %r0,1(%r15) /* add TOD timestamp */
al %r1,5(%r15)
brc 12,25f
ahi %r0,1
j 25f
24: lm %r0,%r1,1(%r15) /* load TOD timestamp */
25: s %r0,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */
sl %r1,__VDSO_XTIME_STAMP+4(%r5) sl %r1,__VDSO_XTIME_STAMP+4(%r5)
brc 3,12f brc 3,12f
ahi %r0,-1 ahi %r0,-1
......
...@@ -31,8 +31,27 @@ __kernel_gettimeofday: ...@@ -31,8 +31,27 @@ __kernel_gettimeofday:
tml %r4,0x0001 /* pending update ? loop */ tml %r4,0x0001 /* pending update ? loop */
jnz 1b jnz 1b
stcke 0(%r15) /* Store TOD clock */ stcke 0(%r15) /* Store TOD clock */
lm %r0,%r1,1(%r15) lm %r0,%r1,__VDSO_TS_END(%r5) /* TOD steering end time */
s %r0,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */ s %r0,1(%r15)
sl %r1,5(%r15)
brc 3,14f
ahi %r0,-1
14: ltr %r0,%r0 /* past end of steering? */
jm 16f
srdl %r0,15 /* 1 per 2^16 */
tm __VDSO_TS_DIR+3(%r5),0x01 /* steering direction? */
jz 15f
lcr %r0,%r0 /* negative TOD offset */
lcr %r1,%r1
je 15f
ahi %r0,-1
15: a %r0,1(%r15) /* add TOD timestamp */
al %r1,5(%r15)
brc 12,17f
ahi %r0,1
j 17f
16: lm %r0,%r1,1(%r15) /* load TOD timestamp */
17: s %r0,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */
sl %r1,__VDSO_XTIME_STAMP+4(%r5) sl %r1,__VDSO_XTIME_STAMP+4(%r5)
brc 3,3f brc 3,3f
ahi %r0,-1 ahi %r0,-1
......
...@@ -83,8 +83,17 @@ __kernel_clock_gettime: ...@@ -83,8 +83,17 @@ __kernel_clock_gettime:
tmll %r4,0x0001 /* pending update ? loop */ tmll %r4,0x0001 /* pending update ? loop */
jnz 5b jnz 5b
stcke 0(%r15) /* Store TOD clock */ stcke 0(%r15) /* Store TOD clock */
lgf %r2,__VDSO_TK_SHIFT(%r5) /* Timekeeper shift */
lg %r1,1(%r15) lg %r1,1(%r15)
lg %r0,__VDSO_TS_END(%r5) /* TOD steering end time */
slgr %r0,%r1 /* now - ts_steering_end */
ltgr %r0,%r0 /* past end of steering ? */
jm 17f
srlg %r0,%r0,15 /* 1 per 2^16 */
tm __VDSO_TS_DIR+3(%r5),0x01 /* steering direction? */
jz 18f
lcgr %r0,%r0 /* negative TOD offset */
18: algr %r1,%r0 /* add steering offset */
17: lgf %r2,__VDSO_TK_SHIFT(%r5) /* Timekeeper shift */
sg %r1,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */ sg %r1,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */
msgf %r1,__VDSO_TK_MULT(%r5) /* * tk->mult */ msgf %r1,__VDSO_TK_MULT(%r5) /* * tk->mult */
alg %r1,__VDSO_XTIME_NSEC(%r5) /* + tk->xtime_nsec */ alg %r1,__VDSO_XTIME_NSEC(%r5) /* + tk->xtime_nsec */
......
...@@ -31,7 +31,16 @@ __kernel_gettimeofday: ...@@ -31,7 +31,16 @@ __kernel_gettimeofday:
jnz 0b jnz 0b
stcke 0(%r15) /* Store TOD clock */ stcke 0(%r15) /* Store TOD clock */
lg %r1,1(%r15) lg %r1,1(%r15)
sg %r1,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */ lg %r0,__VDSO_TS_END(%r5) /* TOD steering end time */
slgr %r0,%r1 /* now - ts_steering_end */
ltgr %r0,%r0 /* past end of steering ? */
jm 6f
srlg %r0,%r0,15 /* 1 per 2^16 */
tm __VDSO_TS_DIR+3(%r5),0x01 /* steering direction? */
jz 7f
lcgr %r0,%r0 /* negative TOD offset */
7: algr %r1,%r0 /* add steering offset */
6: sg %r1,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */
msgf %r1,__VDSO_TK_MULT(%r5) /* * tk->mult */ msgf %r1,__VDSO_TK_MULT(%r5) /* * tk->mult */
alg %r1,__VDSO_XTIME_NSEC(%r5) /* + tk->xtime_nsec */ alg %r1,__VDSO_XTIME_NSEC(%r5) /* + tk->xtime_nsec */
lg %r0,__VDSO_XTIME_SEC(%r5) /* tk->xtime_sec */ lg %r0,__VDSO_XTIME_SEC(%r5) /* tk->xtime_sec */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment