Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

s390/time: add support for the TOD clock epoch extension

The TOD epoch extension adds 8 epoch bits to the TOD clock to provide
a continuous clock after 2042/09/17. The store-clock-extended (STCKE)
instruction will store the epoch index in the first byte of the
16 bytes stored by the instruction. The read_boot_clock64 and the
read_presistent_clock64 functions need to take the additional bits
into account to give the correct result after 2042/09/17.

The clock-comparator register will stay 64 bit wide. The comparison
of the clock-comparator with the TOD clock is limited to bytes
1 to 8 of the extended TOD format. To deal with the overflow problem
due to an epoch change the clock-comparator sign control in CR0 can
be used to switch the comparison of the 64-bit TOD clock with the
clock-comparator to a signed comparison.

The decision between the signed vs. unsigned clock-comparator
comparisons is done at boot time. Only if the TOD clock is in the
second half of a 142 year epoch the signed comparison is used.
This solves the epoch overflow issue as long as the machine is
booted at least once in an epoch.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>

+130 -62
+24 -24
arch/s390/include/asm/lowcore.h
··· 95 95 __u64 int_clock; /* 0x0310 */ 96 96 __u64 mcck_clock; /* 0x0318 */ 97 97 __u64 clock_comparator; /* 0x0320 */ 98 + __u64 boot_clock[2]; /* 0x0328 */ 98 99 99 100 /* Current process. */ 100 - __u64 current_task; /* 0x0328 */ 101 - __u8 pad_0x318[0x320-0x318]; /* 0x0330 */ 102 - __u64 kernel_stack; /* 0x0338 */ 101 + __u64 current_task; /* 0x0338 */ 102 + __u64 kernel_stack; /* 0x0340 */ 103 103 104 104 /* Interrupt, panic and restart stack. */ 105 - __u64 async_stack; /* 0x0340 */ 106 - __u64 panic_stack; /* 0x0348 */ 107 - __u64 restart_stack; /* 0x0350 */ 105 + __u64 async_stack; /* 0x0348 */ 106 + __u64 panic_stack; /* 0x0350 */ 107 + __u64 restart_stack; /* 0x0358 */ 108 108 109 109 /* Restart function and parameter. */ 110 - __u64 restart_fn; /* 0x0358 */ 111 - __u64 restart_data; /* 0x0360 */ 112 - __u64 restart_source; /* 0x0368 */ 110 + __u64 restart_fn; /* 0x0360 */ 111 + __u64 restart_data; /* 0x0368 */ 112 + __u64 restart_source; /* 0x0370 */ 113 113 114 114 /* Address space pointer. */ 115 - __u64 kernel_asce; /* 0x0370 */ 116 - __u64 user_asce; /* 0x0378 */ 115 + __u64 kernel_asce; /* 0x0378 */ 116 + __u64 user_asce; /* 0x0380 */ 117 117 118 118 /* 119 119 * The lpp and current_pid fields form a 120 120 * 64-bit value that is set as program 121 121 * parameter with the LPP instruction. 122 122 */ 123 - __u32 lpp; /* 0x0380 */ 124 - __u32 current_pid; /* 0x0384 */ 123 + __u32 lpp; /* 0x0388 */ 124 + __u32 current_pid; /* 0x038c */ 125 125 126 126 /* SMP info area */ 127 - __u32 cpu_nr; /* 0x0388 */ 128 - __u32 softirq_pending; /* 0x038c */ 129 - __u64 percpu_offset; /* 0x0390 */ 130 - __u64 vdso_per_cpu_data; /* 0x0398 */ 131 - __u64 machine_flags; /* 0x03a0 */ 132 - __u32 preempt_count; /* 0x03a8 */ 133 - __u8 pad_0x03ac[0x03b0-0x03ac]; /* 0x03ac */ 134 - __u64 gmap; /* 0x03b0 */ 135 - __u32 spinlock_lockval; /* 0x03b8 */ 136 - __u32 fpu_flags; /* 0x03bc */ 137 - __u8 pad_0x03c0[0x0400-0x03c0]; /* 0x03c0 */ 127 + __u32 cpu_nr; /* 0x0390 */ 128 + __u32 softirq_pending; /* 0x0394 */ 129 + __u64 percpu_offset; /* 0x0398 */ 130 + __u64 vdso_per_cpu_data; /* 0x03a0 */ 131 + __u64 machine_flags; /* 0x03a8 */ 132 + __u32 preempt_count; /* 0x03b0 */ 133 + __u8 pad_0x03b4[0x03b8-0x03b4]; /* 0x03b4 */ 134 + __u64 gmap; /* 0x03b8 */ 135 + __u32 spinlock_lockval; /* 0x03c0 */ 136 + __u32 fpu_flags; /* 0x03c4 */ 137 + __u8 pad_0x03c8[0x0400-0x03c8]; /* 0x03c8 */ 138 138 139 139 /* Per cpu primary space access list */ 140 140 __u32 paste[16]; /* 0x0400 */
+2
arch/s390/include/asm/setup.h
··· 32 32 #define MACHINE_FLAG_TLB_GUEST _BITUL(14) 33 33 #define MACHINE_FLAG_NX _BITUL(15) 34 34 #define MACHINE_FLAG_GS _BITUL(16) 35 + #define MACHINE_FLAG_SCC _BITUL(17) 35 36 36 37 #define LPP_MAGIC _BITUL(31) 37 38 #define LPP_PFAULT_PID_MASK _AC(0xffffffff, UL) ··· 73 72 #define MACHINE_HAS_TLB_GUEST (S390_lowcore.machine_flags & MACHINE_FLAG_TLB_GUEST) 74 73 #define MACHINE_HAS_NX (S390_lowcore.machine_flags & MACHINE_FLAG_NX) 75 74 #define MACHINE_HAS_GS (S390_lowcore.machine_flags & MACHINE_FLAG_GS) 75 + #define MACHINE_HAS_SCC (S390_lowcore.machine_flags & MACHINE_FLAG_SCC) 76 76 77 77 /* 78 78 * Console mode. Override with conmode=
+34 -4
arch/s390/include/asm/timex.h
··· 15 15 /* The value of the TOD clock for 1.1.1970. */ 16 16 #define TOD_UNIX_EPOCH 0x7d91048bca000000ULL 17 17 18 + extern u64 clock_comparator_max; 19 + 18 20 /* Inline functions for clock register access. */ 19 21 static inline int set_tod_clock(__u64 time) 20 22 { ··· 128 126 unsigned long long old; 129 127 130 128 old = S390_lowcore.clock_comparator; 131 - S390_lowcore.clock_comparator = -1ULL; 129 + S390_lowcore.clock_comparator = clock_comparator_max; 132 130 set_clock_comparator(S390_lowcore.clock_comparator); 133 131 return old; 134 132 } ··· 180 178 void init_cpu_timer(void); 181 179 unsigned long long monotonic_clock(void); 182 180 183 - extern u64 sched_clock_base_cc; 181 + extern unsigned char tod_clock_base[16] __aligned(8); 184 182 185 183 /** 186 184 * get_clock_monotonic - returns current time in clock rate units 187 185 * 188 186 * The caller must ensure that preemption is disabled. 189 - * The clock and sched_clock_base get changed via stop_machine. 187 + * The clock and tod_clock_base get changed via stop_machine. 190 188 * Therefore preemption must be disabled when calling this 191 189 * function, otherwise the returned value is not guaranteed to 192 190 * be monotonic. 193 191 */ 194 192 static inline unsigned long long get_tod_clock_monotonic(void) 195 193 { 196 - return get_tod_clock() - sched_clock_base_cc; 194 + return get_tod_clock() - *(unsigned long long *) &tod_clock_base[1]; 197 195 } 198 196 199 197 /** ··· 218 216 static inline unsigned long long tod_to_ns(unsigned long long todval) 219 217 { 220 218 return ((todval >> 9) * 125) + (((todval & 0x1ff) * 125) >> 9); 219 + } 220 + 221 + /** 222 + * tod_after - compare two 64 bit TOD values 223 + * @a: first 64 bit TOD timestamp 224 + * @b: second 64 bit TOD timestamp 225 + * 226 + * Returns: true if a is later than b 227 + */ 228 + static inline int tod_after(unsigned long long a, unsigned long long b) 229 + { 230 + if (MACHINE_HAS_SCC) 231 + return (long long) a > (long long) b; 232 + return a > b; 233 + } 234 + 235 + /** 236 + * tod_after_eq - compare two 64 bit TOD values 237 + * @a: first 64 bit TOD timestamp 238 + * @b: second 64 bit TOD timestamp 239 + * 240 + * Returns: true if a is later than b 241 + */ 242 + static inline int tod_after_eq(unsigned long long a, unsigned long long b) 243 + { 244 + if (MACHINE_HAS_SCC) 245 + return (long long) a >= (long long) b; 246 + return a >= b; 221 247 } 222 248 223 249 #endif
+1
arch/s390/kernel/asm-offsets.c
··· 158 158 OFFSET(__LC_LAST_UPDATE_CLOCK, lowcore, last_update_clock); 159 159 OFFSET(__LC_INT_CLOCK, lowcore, int_clock); 160 160 OFFSET(__LC_MCCK_CLOCK, lowcore, mcck_clock); 161 + OFFSET(__LC_BOOT_CLOCK, lowcore, boot_clock); 161 162 OFFSET(__LC_CURRENT, lowcore, current_task); 162 163 OFFSET(__LC_KERNEL_STACK, lowcore, kernel_stack); 163 164 OFFSET(__LC_ASYNC_STACK, lowcore, async_stack);
+5 -4
arch/s390/kernel/debug.c
··· 866 866 debug_finish_entry(debug_info_t * id, debug_entry_t* active, int level, 867 867 int exception) 868 868 { 869 - active->id.stck = get_tod_clock_fast() - sched_clock_base_cc; 869 + active->id.stck = get_tod_clock_fast() - 870 + *(unsigned long long *) &tod_clock_base[1]; 870 871 active->id.fields.cpuid = smp_processor_id(); 871 872 active->caller = __builtin_return_address(0); 872 873 active->id.fields.exception = exception; ··· 1456 1455 debug_dflt_header_fn(debug_info_t * id, struct debug_view *view, 1457 1456 int area, debug_entry_t * entry, char *out_buf) 1458 1457 { 1459 - unsigned long sec, usec; 1458 + unsigned long base, sec, usec; 1460 1459 char *except_str; 1461 1460 unsigned long caller; 1462 1461 int rc = 0; 1463 1462 unsigned int level; 1464 1463 1465 1464 level = entry->id.fields.level; 1466 - sec = (entry->id.stck >> 12) + (sched_clock_base_cc >> 12); 1467 - sec = sec - (TOD_UNIX_EPOCH >> 12); 1465 + base = (*(unsigned long *) &tod_clock_base[0]) >> 4; 1466 + sec = (entry->id.stck >> 12) + base - (TOD_UNIX_EPOCH >> 12); 1468 1467 usec = do_div(sec, USEC_PER_SEC); 1469 1468 1470 1469 if (entry->id.fields.exception)
+11 -4
arch/s390/kernel/early.c
··· 53 53 if (set_tod_clock(TOD_UNIX_EPOCH) != 0 || store_tod_clock(&time) != 0) 54 54 disabled_wait(0); 55 55 56 - sched_clock_base_cc = TOD_UNIX_EPOCH; 57 - S390_lowcore.last_update_clock = sched_clock_base_cc; 56 + memset(tod_clock_base, 0, 16); 57 + *(__u64 *) &tod_clock_base[1] = TOD_UNIX_EPOCH; 58 + S390_lowcore.last_update_clock = TOD_UNIX_EPOCH; 58 59 } 59 60 60 61 #ifdef CONFIG_SHARED_KERNEL ··· 166 165 } 167 166 168 167 /* re-initialize cputime accounting. */ 169 - sched_clock_base_cc = get_tod_clock(); 170 - S390_lowcore.last_update_clock = sched_clock_base_cc; 168 + get_tod_clock_ext(tod_clock_base); 169 + S390_lowcore.last_update_clock = *(__u64 *) &tod_clock_base[1]; 171 170 S390_lowcore.last_update_timer = 0x7fffffffffffffffULL; 172 171 S390_lowcore.user_timer = 0; 173 172 S390_lowcore.system_timer = 0; ··· 388 387 } 389 388 if (test_facility(133)) 390 389 S390_lowcore.machine_flags |= MACHINE_FLAG_GS; 390 + if (test_facility(139) && (tod_clock_base[1] & 0x80)) { 391 + /* Enabled signed clock comparator comparisons */ 392 + S390_lowcore.machine_flags |= MACHINE_FLAG_SCC; 393 + clock_comparator_max = -1ULL >> 1; 394 + __ctl_set_bit(0, 53); 395 + } 391 396 } 392 397 393 398 static inline void save_vector_registers(void)
+2 -1
arch/s390/kernel/head.S
··· 302 302 xc 0xe00(256),0xe00 303 303 xc 0xf00(256),0xf00 304 304 lctlg %c0,%c15,0x200(%r0) # initialize control registers 305 - stck __LC_LAST_UPDATE_CLOCK 305 + stcke __LC_BOOT_CLOCK 306 + mvc __LC_LAST_UPDATE_CLOCK(8),__LC_BOOT_CLOCK+1 306 307 spt 6f-.LPG0(%r13) 307 308 mvc __LC_LAST_UPDATE_TIMER(8),6f-.LPG0(%r13) 308 309 l %r15,.Lstack-.LPG0(%r13)
+2 -2
arch/s390/kernel/head64.S
··· 21 21 xc __LC_LPP+1(7,0),__LC_LPP+1 # clear lpp and current_pid 22 22 mvi __LC_LPP,0x80 # and set LPP_MAGIC 23 23 .insn s,0xb2800000,__LC_LPP # load program parameter 24 - 0: larl %r1,sched_clock_base_cc 25 - mvc 0(8,%r1),__LC_LAST_UPDATE_CLOCK 24 + 0: larl %r1,tod_clock_base 25 + mvc 0(16,%r1),__LC_BOOT_CLOCK 26 26 larl %r13,.LPG1 # get base 27 27 lctlg %c0,%c15,.Lctl-.LPG1(%r13) # load control registers 28 28 lg %r12,.Lparmaddr-.LPG1(%r13) # pointer to parameter area
+2 -1
arch/s390/kernel/irq.c
··· 105 105 106 106 old_regs = set_irq_regs(regs); 107 107 irq_enter(); 108 - if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator) 108 + if (tod_after_eq(S390_lowcore.int_clock, 109 + S390_lowcore.clock_comparator)) 109 110 /* Serve timer interrupts first. */ 110 111 clock_comparator_work(); 111 112 generic_handle_irq(irq);
+1 -1
arch/s390/kernel/setup.c
··· 323 323 lc->io_new_psw.mask = PSW_KERNEL_BITS | 324 324 PSW_MASK_DAT | PSW_MASK_MCHECK; 325 325 lc->io_new_psw.addr = (unsigned long) io_int_handler; 326 - lc->clock_comparator = -1ULL; 326 + lc->clock_comparator = clock_comparator_max; 327 327 lc->kernel_stack = ((unsigned long) &init_thread_union) 328 328 + THREAD_SIZE - STACK_FRAME_OVERHEAD - sizeof(struct pt_regs); 329 329 lc->async_stack = (unsigned long)
+45 -20
arch/s390/kernel/time.c
··· 51 51 #include <asm/cio.h> 52 52 #include "entry.h" 53 53 54 - u64 sched_clock_base_cc = -1; /* Force to data section. */ 55 - EXPORT_SYMBOL_GPL(sched_clock_base_cc); 54 + unsigned char tod_clock_base[16] __aligned(8) = { 55 + /* Force to data section. */ 56 + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 57 + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff 58 + }; 59 + EXPORT_SYMBOL_GPL(tod_clock_base); 60 + 61 + u64 clock_comparator_max = -1ULL; 62 + EXPORT_SYMBOL_GPL(clock_comparator_max); 56 63 57 64 static DEFINE_PER_CPU(struct clock_event_device, comparators); 58 65 ··· 82 75 struct ptff_qui qui; 83 76 84 77 /* Initialize TOD steering parameters */ 85 - tod_steering_end = sched_clock_base_cc; 78 + tod_steering_end = *(unsigned long long *) &tod_clock_base[1]; 86 79 vdso_data->ts_end = tod_steering_end; 87 80 88 81 if (!test_facility(28)) ··· 118 111 } 119 112 EXPORT_SYMBOL(monotonic_clock); 120 113 121 - static void tod_to_timeval(__u64 todval, struct timespec64 *xt) 114 + static void ext_to_timespec64(unsigned char *clk, struct timespec64 *xt) 122 115 { 123 - unsigned long long sec; 116 + unsigned long long high, low, rem, sec, nsec; 124 117 125 - sec = todval >> 12; 126 - do_div(sec, 1000000); 118 + /* Split extendnd TOD clock to micro-seconds and sub-micro-seconds */ 119 + high = (*(unsigned long long *) clk) >> 4; 120 + low = (*(unsigned long long *)&clk[7]) << 4; 121 + /* Calculate seconds and nano-seconds */ 122 + sec = high; 123 + rem = do_div(sec, 1000000); 124 + nsec = (((low >> 32) + (rem << 32)) * 1000) >> 32; 125 + 127 126 xt->tv_sec = sec; 128 - todval -= (sec * 1000000) << 12; 129 - xt->tv_nsec = ((todval * 1000) >> 12); 127 + xt->tv_nsec = nsec; 130 128 } 131 129 132 130 void clock_comparator_work(void) 133 131 { 134 132 struct clock_event_device *cd; 135 133 136 - S390_lowcore.clock_comparator = -1ULL; 134 + S390_lowcore.clock_comparator = clock_comparator_max; 137 135 cd = this_cpu_ptr(&comparators); 138 136 cd->event_handler(cd); 139 137 } ··· 160 148 struct clock_event_device *cd; 161 149 int cpu; 162 150 163 - S390_lowcore.clock_comparator = -1ULL; 151 + S390_lowcore.clock_comparator = clock_comparator_max; 164 152 set_clock_comparator(S390_lowcore.clock_comparator); 165 153 166 154 cpu = smp_processor_id(); ··· 191 179 unsigned long param64) 192 180 { 193 181 inc_irq_stat(IRQEXT_CLK); 194 - if (S390_lowcore.clock_comparator == -1ULL) 182 + if (S390_lowcore.clock_comparator == clock_comparator_max) 195 183 set_clock_comparator(S390_lowcore.clock_comparator); 196 184 } 197 185 ··· 209 197 210 198 void read_persistent_clock64(struct timespec64 *ts) 211 199 { 212 - __u64 clock; 200 + unsigned char clk[STORE_CLOCK_EXT_SIZE]; 201 + __u64 delta; 213 202 214 - clock = get_tod_clock() - initial_leap_seconds; 215 - tod_to_timeval(clock - TOD_UNIX_EPOCH, ts); 203 + delta = initial_leap_seconds + TOD_UNIX_EPOCH; 204 + get_tod_clock_ext(clk); 205 + *(__u64 *) &clk[1] -= delta; 206 + if (*(__u64 *) &clk[1] > delta) 207 + clk[0]--; 208 + ext_to_timespec64(clk, ts); 216 209 } 217 210 218 211 void read_boot_clock64(struct timespec64 *ts) 219 212 { 220 - __u64 clock; 213 + unsigned char clk[STORE_CLOCK_EXT_SIZE]; 214 + __u64 delta; 221 215 222 - clock = sched_clock_base_cc - initial_leap_seconds; 223 - tod_to_timeval(clock - TOD_UNIX_EPOCH, ts); 216 + delta = initial_leap_seconds + TOD_UNIX_EPOCH; 217 + memcpy(clk, tod_clock_base, 16); 218 + *(__u64 *) &clk[1] -= delta; 219 + if (*(__u64 *) &clk[1] > delta) 220 + clk[0]--; 221 + ext_to_timespec64(clk, ts); 224 222 } 225 223 226 224 static u64 read_tod_clock(struct clocksource *cs) ··· 428 406 struct ptff_qto qto; 429 407 430 408 /* Fixup the monotonic sched clock. */ 431 - sched_clock_base_cc += delta; 409 + *(unsigned long long *) &tod_clock_base[1] += delta; 410 + if (*(unsigned long long *) &tod_clock_base[1] < delta) 411 + /* Epoch overflow */ 412 + tod_clock_base[0]++; 432 413 /* Adjust TOD steering parameters. */ 433 414 vdso_data->tb_update_count++; 434 415 now = get_tod_clock(); ··· 462 437 static void clock_sync_local(unsigned long long delta) 463 438 { 464 439 /* Add the delta to the clock comparator. */ 465 - if (S390_lowcore.clock_comparator != -1ULL) { 440 + if (S390_lowcore.clock_comparator != clock_comparator_max) { 466 441 S390_lowcore.clock_comparator += delta; 467 442 set_clock_comparator(S390_lowcore.clock_comparator); 468 443 }
+1 -1
arch/s390/lib/delay.c
··· 57 57 end = get_tod_clock_fast() + (usecs << 12); 58 58 do { 59 59 clock_saved = 0; 60 - if (end < S390_lowcore.clock_comparator) { 60 + if (tod_after(S390_lowcore.clock_comparator, end)) { 61 61 clock_saved = local_tick_disable(); 62 62 set_clock_comparator(end); 63 63 }