aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390/kernel/time.c
diff options
context:
space:
mode:
authorMartin Schwidefsky <schwidefsky@de.ibm.com>2016-05-31 04:16:24 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2016-06-13 09:58:20 -0400
commit4027789192d149678262ad606b2d7e2a61bed0f2 (patch)
treecb58f29b1ddf8e1f06eee12c67fa2a846ee0e7ad /arch/s390/kernel/time.c
parent9dc06ccf4699db81b88a6ff45a8acefd6c278327 (diff)
s390/time: LPAR offset handling
It is possible to specify a user offset for the TOD clock, e.g. +2 hours. The TOD clock will carry this offset even if the clock is synchronized with STP. This makes the time stamps acquired with get_sync_clock() useless as another LPAR migth use a different TOD offset. Use the PTFF instrution to get the TOD epoch difference and subtract it from the TOD clock value to get a physical timestamp. As the epoch difference contains the sync check delta as well the LPAR offset value to the physical clock needs to be refreshed after each clock synchronization. Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390/kernel/time.c')
-rw-r--r--arch/s390/kernel/time.c46
1 files changed, 38 insertions, 8 deletions
diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c
index a725fd1c4315..d71623639997 100644
--- a/arch/s390/kernel/time.c
+++ b/arch/s390/kernel/time.c
@@ -39,6 +39,7 @@
39#include <linux/gfp.h> 39#include <linux/gfp.h>
40#include <linux/kprobes.h> 40#include <linux/kprobes.h>
41#include <asm/uaccess.h> 41#include <asm/uaccess.h>
42#include <asm/facility.h>
42#include <asm/delay.h> 43#include <asm/delay.h>
43#include <asm/div64.h> 44#include <asm/div64.h>
44#include <asm/vdso.h> 45#include <asm/vdso.h>
@@ -61,6 +62,25 @@ static DEFINE_PER_CPU(struct clock_event_device, comparators);
61ATOMIC_NOTIFIER_HEAD(s390_epoch_delta_notifier); 62ATOMIC_NOTIFIER_HEAD(s390_epoch_delta_notifier);
62EXPORT_SYMBOL(s390_epoch_delta_notifier); 63EXPORT_SYMBOL(s390_epoch_delta_notifier);
63 64
65unsigned char ptff_function_mask[16];
66unsigned long lpar_offset;
67
68/*
69 * Get time offsets with PTFF
70 */
71void __init ptff_init(void)
72{
73 struct ptff_qto qto;
74
75 if (!test_facility(28))
76 return;
77 ptff(&ptff_function_mask, sizeof(ptff_function_mask), PTFF_QAF);
78
79 /* get LPAR offset */
80 if (ptff_query(PTFF_QTO) && ptff(&qto, sizeof(qto), PTFF_QTO) == 0)
81 lpar_offset = qto.tod_epoch_difference;
82}
83
64/* 84/*
65 * Scheduler clock - returns current time in nanosec units. 85 * Scheduler clock - returns current time in nanosec units.
66 */ 86 */
@@ -337,20 +357,20 @@ static unsigned long clock_sync_flags;
337#define CLOCK_SYNC_STP 3 357#define CLOCK_SYNC_STP 3
338 358
339/* 359/*
340 * The synchronous get_clock function. It will write the current clock 360 * The get_clock function for the physical clock. It will get the current
341 * value to the clock pointer and return 0 if the clock is in sync with 361 * TOD clock, subtract the LPAR offset and write the result to *clock.
342 * the external time source. If the clock mode is local it will return 362 * The function returns 0 if the clock is in sync with the external time
343 * -EOPNOTSUPP and -EAGAIN if the clock is not in sync with the external 363 * source. If the clock mode is local it will return -EOPNOTSUPP and
344 * reference. 364 * -EAGAIN if the clock is not in sync with the external reference.
345 */ 365 */
346int get_sync_clock(unsigned long long *clock) 366int get_phys_clock(unsigned long long *clock)
347{ 367{
348 atomic_t *sw_ptr; 368 atomic_t *sw_ptr;
349 unsigned int sw0, sw1; 369 unsigned int sw0, sw1;
350 370
351 sw_ptr = &get_cpu_var(clock_sync_word); 371 sw_ptr = &get_cpu_var(clock_sync_word);
352 sw0 = atomic_read(sw_ptr); 372 sw0 = atomic_read(sw_ptr);
353 *clock = get_tod_clock(); 373 *clock = get_tod_clock() - lpar_offset;
354 sw1 = atomic_read(sw_ptr); 374 sw1 = atomic_read(sw_ptr);
355 put_cpu_var(clock_sync_word); 375 put_cpu_var(clock_sync_word);
356 if (sw0 == sw1 && (sw0 & 0x80000000U)) 376 if (sw0 == sw1 && (sw0 & 0x80000000U))
@@ -364,7 +384,7 @@ int get_sync_clock(unsigned long long *clock)
364 return -EACCES; 384 return -EACCES;
365 return -EAGAIN; 385 return -EAGAIN;
366} 386}
367EXPORT_SYMBOL(get_sync_clock); 387EXPORT_SYMBOL(get_phys_clock);
368 388
369/* 389/*
370 * Make get_sync_clock return -EAGAIN. 390 * Make get_sync_clock return -EAGAIN.
@@ -758,6 +778,7 @@ static int etr_sync_clock(void *data)
758 unsigned long long clock, old_clock, clock_delta, delay, delta; 778 unsigned long long clock, old_clock, clock_delta, delay, delta;
759 struct clock_sync_data *etr_sync; 779 struct clock_sync_data *etr_sync;
760 struct etr_aib *sync_port, *aib; 780 struct etr_aib *sync_port, *aib;
781 struct ptff_qto qto;
761 int port; 782 int port;
762 int rc; 783 int rc;
763 784
@@ -804,6 +825,10 @@ static int etr_sync_clock(void *data)
804 etr_sync->in_sync = -EAGAIN; 825 etr_sync->in_sync = -EAGAIN;
805 rc = -EAGAIN; 826 rc = -EAGAIN;
806 } else { 827 } else {
828 if (ptff_query(PTFF_QTO) &&
829 ptff(&qto, sizeof(qto), PTFF_QTO) == 0)
830 /* Update LPAR offset */
831 lpar_offset = qto.tod_epoch_difference;
807 etr_sync->in_sync = 1; 832 etr_sync->in_sync = 1;
808 rc = 0; 833 rc = 0;
809 } 834 }
@@ -1533,6 +1558,7 @@ static int stp_sync_clock(void *data)
1533 static int first; 1558 static int first;
1534 unsigned long long old_clock, delta, new_clock, clock_delta; 1559 unsigned long long old_clock, delta, new_clock, clock_delta;
1535 struct clock_sync_data *stp_sync; 1560 struct clock_sync_data *stp_sync;
1561 struct ptff_qto qto;
1536 int rc; 1562 int rc;
1537 1563
1538 stp_sync = data; 1564 stp_sync = data;
@@ -1558,6 +1584,10 @@ static int stp_sync_clock(void *data)
1558 if (rc == 0) { 1584 if (rc == 0) {
1559 new_clock = old_clock + clock_delta; 1585 new_clock = old_clock + clock_delta;
1560 delta = adjust_time(old_clock, new_clock, 0); 1586 delta = adjust_time(old_clock, new_clock, 0);
1587 if (ptff_query(PTFF_QTO) &&
1588 ptff(&qto, sizeof(qto), PTFF_QTO) == 0)
1589 /* Update LPAR offset */
1590 lpar_offset = qto.tod_epoch_difference;
1561 atomic_notifier_call_chain(&s390_epoch_delta_notifier, 1591 atomic_notifier_call_chain(&s390_epoch_delta_notifier,
1562 0, &clock_delta); 1592 0, &clock_delta);
1563 fixup_clock_comparator(delta); 1593 fixup_clock_comparator(delta);