Commit 40277891 authored by Martin Schwidefsky's avatar Martin Schwidefsky

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: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 9dc06ccf
...@@ -52,6 +52,11 @@ static inline void store_clock_comparator(__u64 *time) ...@@ -52,6 +52,11 @@ static inline void store_clock_comparator(__u64 *time)
void clock_comparator_work(void); void clock_comparator_work(void);
void __init ptff_init(void);
extern unsigned char ptff_function_mask[16];
extern unsigned long lpar_offset;
/* Function codes for the ptff instruction. */ /* Function codes for the ptff instruction. */
#define PTFF_QAF 0x00 /* query available functions */ #define PTFF_QAF 0x00 /* query available functions */
#define PTFF_QTO 0x01 /* query tod offset */ #define PTFF_QTO 0x01 /* query tod offset */
...@@ -69,6 +74,14 @@ struct ptff_qto { ...@@ -69,6 +74,14 @@ struct ptff_qto {
unsigned long long tod_epoch_difference; unsigned long long tod_epoch_difference;
} __packed; } __packed;
static inline int ptff_query(unsigned int nr)
{
unsigned char *ptr;
ptr = ptff_function_mask + (nr >> 3);
return (*ptr & (0x80 >> (nr & 7))) != 0;
}
static inline int ptff(void *ptff_block, size_t len, unsigned int func) static inline int ptff(void *ptff_block, size_t len, unsigned int func)
{ {
typedef struct { char _[len]; } addrtype; typedef struct { char _[len]; } addrtype;
...@@ -138,7 +151,7 @@ static inline cycles_t get_cycles(void) ...@@ -138,7 +151,7 @@ static inline cycles_t get_cycles(void)
return (cycles_t) get_tod_clock() >> 2; return (cycles_t) get_tod_clock() >> 2;
} }
int get_sync_clock(unsigned long long *clock); int get_phys_clock(unsigned long long *clock);
void init_cpu_timer(void); void init_cpu_timer(void);
unsigned long long monotonic_clock(void); unsigned long long monotonic_clock(void);
......
...@@ -467,6 +467,7 @@ void __init startup_init(void) ...@@ -467,6 +467,7 @@ void __init startup_init(void)
ipl_save_parameters(); ipl_save_parameters();
rescue_initrd(); rescue_initrd();
clear_bss_section(); clear_bss_section();
ptff_init();
init_kernel_storage_key(); init_kernel_storage_key();
lockdep_off(); lockdep_off();
setup_lowcore_early(); setup_lowcore_early();
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#include <linux/gfp.h> #include <linux/gfp.h>
#include <linux/kprobes.h> #include <linux/kprobes.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/facility.h>
#include <asm/delay.h> #include <asm/delay.h>
#include <asm/div64.h> #include <asm/div64.h>
#include <asm/vdso.h> #include <asm/vdso.h>
...@@ -61,6 +62,25 @@ static DEFINE_PER_CPU(struct clock_event_device, comparators); ...@@ -61,6 +62,25 @@ static DEFINE_PER_CPU(struct clock_event_device, comparators);
ATOMIC_NOTIFIER_HEAD(s390_epoch_delta_notifier); ATOMIC_NOTIFIER_HEAD(s390_epoch_delta_notifier);
EXPORT_SYMBOL(s390_epoch_delta_notifier); EXPORT_SYMBOL(s390_epoch_delta_notifier);
unsigned char ptff_function_mask[16];
unsigned long lpar_offset;
/*
* Get time offsets with PTFF
*/
void __init ptff_init(void)
{
struct ptff_qto qto;
if (!test_facility(28))
return;
ptff(&ptff_function_mask, sizeof(ptff_function_mask), PTFF_QAF);
/* get LPAR offset */
if (ptff_query(PTFF_QTO) && ptff(&qto, sizeof(qto), PTFF_QTO) == 0)
lpar_offset = qto.tod_epoch_difference;
}
/* /*
* Scheduler clock - returns current time in nanosec units. * Scheduler clock - returns current time in nanosec units.
*/ */
...@@ -337,20 +357,20 @@ static unsigned long clock_sync_flags; ...@@ -337,20 +357,20 @@ static unsigned long clock_sync_flags;
#define CLOCK_SYNC_STP 3 #define CLOCK_SYNC_STP 3
/* /*
* The synchronous get_clock function. It will write the current clock * The get_clock function for the physical clock. It will get the current
* value to the clock pointer and return 0 if the clock is in sync with * TOD clock, subtract the LPAR offset and write the result to *clock.
* the external time source. If the clock mode is local it will return * The function returns 0 if the clock is in sync with the external time
* -EOPNOTSUPP and -EAGAIN if the clock is not in sync with the external * source. If the clock mode is local it will return -EOPNOTSUPP and
* reference. * -EAGAIN if the clock is not in sync with the external reference.
*/ */
int get_sync_clock(unsigned long long *clock) int get_phys_clock(unsigned long long *clock)
{ {
atomic_t *sw_ptr; atomic_t *sw_ptr;
unsigned int sw0, sw1; unsigned int sw0, sw1;
sw_ptr = &get_cpu_var(clock_sync_word); sw_ptr = &get_cpu_var(clock_sync_word);
sw0 = atomic_read(sw_ptr); sw0 = atomic_read(sw_ptr);
*clock = get_tod_clock(); *clock = get_tod_clock() - lpar_offset;
sw1 = atomic_read(sw_ptr); sw1 = atomic_read(sw_ptr);
put_cpu_var(clock_sync_word); put_cpu_var(clock_sync_word);
if (sw0 == sw1 && (sw0 & 0x80000000U)) if (sw0 == sw1 && (sw0 & 0x80000000U))
...@@ -364,7 +384,7 @@ int get_sync_clock(unsigned long long *clock) ...@@ -364,7 +384,7 @@ int get_sync_clock(unsigned long long *clock)
return -EACCES; return -EACCES;
return -EAGAIN; return -EAGAIN;
} }
EXPORT_SYMBOL(get_sync_clock); EXPORT_SYMBOL(get_phys_clock);
/* /*
* Make get_sync_clock return -EAGAIN. * Make get_sync_clock return -EAGAIN.
...@@ -758,6 +778,7 @@ static int etr_sync_clock(void *data) ...@@ -758,6 +778,7 @@ static int etr_sync_clock(void *data)
unsigned long long clock, old_clock, clock_delta, delay, delta; unsigned long long clock, old_clock, clock_delta, delay, delta;
struct clock_sync_data *etr_sync; struct clock_sync_data *etr_sync;
struct etr_aib *sync_port, *aib; struct etr_aib *sync_port, *aib;
struct ptff_qto qto;
int port; int port;
int rc; int rc;
...@@ -804,6 +825,10 @@ static int etr_sync_clock(void *data) ...@@ -804,6 +825,10 @@ static int etr_sync_clock(void *data)
etr_sync->in_sync = -EAGAIN; etr_sync->in_sync = -EAGAIN;
rc = -EAGAIN; rc = -EAGAIN;
} else { } else {
if (ptff_query(PTFF_QTO) &&
ptff(&qto, sizeof(qto), PTFF_QTO) == 0)
/* Update LPAR offset */
lpar_offset = qto.tod_epoch_difference;
etr_sync->in_sync = 1; etr_sync->in_sync = 1;
rc = 0; rc = 0;
} }
...@@ -1533,6 +1558,7 @@ static int stp_sync_clock(void *data) ...@@ -1533,6 +1558,7 @@ static int stp_sync_clock(void *data)
static int first; static int first;
unsigned long long old_clock, delta, new_clock, clock_delta; unsigned long long old_clock, delta, new_clock, clock_delta;
struct clock_sync_data *stp_sync; struct clock_sync_data *stp_sync;
struct ptff_qto qto;
int rc; int rc;
stp_sync = data; stp_sync = data;
...@@ -1558,6 +1584,10 @@ static int stp_sync_clock(void *data) ...@@ -1558,6 +1584,10 @@ static int stp_sync_clock(void *data)
if (rc == 0) { if (rc == 0) {
new_clock = old_clock + clock_delta; new_clock = old_clock + clock_delta;
delta = adjust_time(old_clock, new_clock, 0); delta = adjust_time(old_clock, new_clock, 0);
if (ptff_query(PTFF_QTO) &&
ptff(&qto, sizeof(qto), PTFF_QTO) == 0)
/* Update LPAR offset */
lpar_offset = qto.tod_epoch_difference;
atomic_notifier_call_chain(&s390_epoch_delta_notifier, atomic_notifier_call_chain(&s390_epoch_delta_notifier,
0, &clock_delta); 0, &clock_delta);
fixup_clock_comparator(delta); fixup_clock_comparator(delta);
......
...@@ -228,7 +228,7 @@ check_XRC (struct ccw1 *de_ccw, ...@@ -228,7 +228,7 @@ check_XRC (struct ccw1 *de_ccw,
data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid' */ data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid' */
data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */ data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */
rc = get_sync_clock(&data->ep_sys_time); rc = get_phys_clock(&data->ep_sys_time);
/* Ignore return code if sync clock is switched off. */ /* Ignore return code if sync clock is switched off. */
if (rc == -EOPNOTSUPP || rc == -EACCES) if (rc == -EOPNOTSUPP || rc == -EACCES)
rc = 0; rc = 0;
...@@ -339,7 +339,7 @@ static int check_XRC_on_prefix(struct PFX_eckd_data *pfxdata, ...@@ -339,7 +339,7 @@ static int check_XRC_on_prefix(struct PFX_eckd_data *pfxdata,
pfxdata->define_extent.ga_extended |= 0x02; /* 'Extended Parameter' */ pfxdata->define_extent.ga_extended |= 0x02; /* 'Extended Parameter' */
pfxdata->validity.time_stamp = 1; /* 'Time Stamp Valid' */ pfxdata->validity.time_stamp = 1; /* 'Time Stamp Valid' */
rc = get_sync_clock(&pfxdata->define_extent.ep_sys_time); rc = get_phys_clock(&pfxdata->define_extent.ep_sys_time);
/* Ignore return code if sync clock is switched off. */ /* Ignore return code if sync clock is switched off. */
if (rc == -EOPNOTSUPP || rc == -EACCES) if (rc == -EOPNOTSUPP || rc == -EACCES)
rc = 0; rc = 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment