diff options
author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2016-05-31 04:16:24 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2016-06-13 09:58:20 -0400 |
commit | 4027789192d149678262ad606b2d7e2a61bed0f2 (patch) | |
tree | cb58f29b1ddf8e1f06eee12c67fa2a846ee0e7ad /arch/s390/kernel/time.c | |
parent | 9dc06ccf4699db81b88a6ff45a8acefd6c278327 (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.c | 46 |
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); | |||
61 | ATOMIC_NOTIFIER_HEAD(s390_epoch_delta_notifier); | 62 | ATOMIC_NOTIFIER_HEAD(s390_epoch_delta_notifier); |
62 | EXPORT_SYMBOL(s390_epoch_delta_notifier); | 63 | EXPORT_SYMBOL(s390_epoch_delta_notifier); |
63 | 64 | ||
65 | unsigned char ptff_function_mask[16]; | ||
66 | unsigned long lpar_offset; | ||
67 | |||
68 | /* | ||
69 | * Get time offsets with PTFF | ||
70 | */ | ||
71 | void __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 | */ |
346 | int get_sync_clock(unsigned long long *clock) | 366 | int 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 | } |
367 | EXPORT_SYMBOL(get_sync_clock); | 387 | EXPORT_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); |