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