s390/time: steer clocksource on STP sync events
authorMartin Schwidefsky <schwidefsky@de.ibm.com>
Tue, 11 Oct 2016 10:49:50 +0000 (12:49 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Fri, 28 Oct 2016 08:09:02 +0000 (10:09 +0200)
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: David Hildenbrand <dahi@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/include/asm/vdso.h
arch/s390/kernel/asm-offsets.c
arch/s390/kernel/time.c
arch/s390/kernel/vdso32/clock_gettime.S
arch/s390/kernel/vdso32/gettimeofday.S
arch/s390/kernel/vdso64/clock_gettime.S
arch/s390/kernel/vdso64/gettimeofday.S

index d0a2dbf2433d0b45c2a3f484b54b8d9528aca8cf..88bdc477a843dde8a7ab08a9f6e8aa9d2219a067 100644 (file)
@@ -33,6 +33,8 @@ struct vdso_data {
        __u32 ectg_available;           /* ECTG instruction present     0x58 */
        __u32 tk_mult;                  /* Mult. used for xtime_nsec    0x5c */
        __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 {
index f3df9e0a5dec6383387097a0edb7cf591077b687..ec16cec6bcd449dc3acd892f72f05b0d18d54722 100644 (file)
@@ -79,6 +79,8 @@ int main(void)
        OFFSET(__VDSO_ECTG_OK, vdso_data, ectg_available);
        OFFSET(__VDSO_TK_MULT, vdso_data, tk_mult);
        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_USER, vdso_per_cpu_data, ectg_user_time);
        OFFSET(__VDSO_CPU_NR, vdso_per_cpu_data, cpu_nr);
index 5ba6c67ddd99c9b332aeb9f925c94d91d528ee7b..33082f6cbb5d14d860bf74c8cdd32ea5a8653482 100644 (file)
@@ -62,6 +62,8 @@ unsigned char ptff_function_mask[16];
 
 static unsigned long long lpar_offset;
 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
@@ -71,8 +73,13 @@ void __init time_early_init(void)
        struct ptff_qto qto;
        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))
                return;
+
        ptff(&ptff_function_mask, sizeof(ptff_function_mask), PTFF_QAF);
 
        /* get LPAR offset */
@@ -204,7 +211,22 @@ void read_boot_clock64(struct timespec64 *ts)
 
 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 = {
@@ -379,10 +401,27 @@ static inline int check_sync_clock(void)
  */
 static void clock_sync_global(unsigned long long delta)
 {
+       unsigned long now, adj;
        struct ptff_qto qto;
 
        /* Fixup the monotonic sched clock. */
        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. */
        if (ptff_query(PTFF_QTO) && ptff(&qto, sizeof(qto), PTFF_QTO) == 0)
                lpar_offset = qto.tod_epoch_difference;
index 5eec9afbb5b5852cdcbf2a50685a43030f3af5f7..a5769b83d90e687f08175af96ded9a4893ebd2d2 100644 (file)
@@ -99,8 +99,27 @@ __kernel_clock_gettime:
        tml     %r4,0x0001                      /* pending update ? loop */
        jnz     11b
        stcke   0(%r15)                         /* Store TOD clock */
-       lm      %r0,%r1,1(%r15)
-       s       %r0,__VDSO_XTIME_STAMP(%r5)     /* TOD - cycle_last */
+       lm      %r0,%r1,__VDSO_TS_END(%r5)      /* TOD steering end time */
+       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)
        brc     3,12f
        ahi     %r0,-1
index 719de6186b206e17c60b973b1661e57cf1505747..63b86dceb0bfec0f72886d96230e5248620f8728 100644 (file)
@@ -31,8 +31,27 @@ __kernel_gettimeofday:
        tml     %r4,0x0001                      /* pending update ? loop */
        jnz     1b
        stcke   0(%r15)                         /* Store TOD clock */
-       lm      %r0,%r1,1(%r15)
-       s       %r0,__VDSO_XTIME_STAMP(%r5)     /* TOD - cycle_last */
+       lm      %r0,%r1,__VDSO_TS_END(%r5)      /* TOD steering end time */
+       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)
        brc     3,3f
        ahi     %r0,-1
index 61541fb93dc63e0f4673dcfe12e9d69681eb0993..9c3b12626dbae06a77a12b4e186a4bb0eb253e09 100644 (file)
@@ -83,8 +83,17 @@ __kernel_clock_gettime:
        tmll    %r4,0x0001                      /* pending update ? loop */
        jnz     5b
        stcke   0(%r15)                         /* Store TOD clock */
-       lgf     %r2,__VDSO_TK_SHIFT(%r5)        /* Timekeeper shift */
        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 */
        msgf    %r1,__VDSO_TK_MULT(%r5)         /*  * tk->mult */
        alg     %r1,__VDSO_XTIME_NSEC(%r5)      /*  + tk->xtime_nsec */
index 6ce46707663cc9d8248e5feedb71a0268d9629f2..b02e62f3bc12d4ffb3850aff587d20ad944f4688 100644 (file)
@@ -31,7 +31,16 @@ __kernel_gettimeofday:
        jnz     0b
        stcke   0(%r15)                         /* Store TOD clock */
        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 */
        alg     %r1,__VDSO_XTIME_NSEC(%r5)      /*  + tk->xtime_nsec */
        lg      %r0,__VDSO_XTIME_SEC(%r5)       /* tk->xtime_sec */