s390x/tcg: properly implement the TOD

Right now, each CPU has its own TOD. Especially, the TOD will differ
based on creation time of a CPU - e.g. when hotplugging a CPU the times
will differ quite a lot, resulting in stall warnings in the guest.

Let's use a single TOD by implementing our new TOD device. Prepare it
for TOD-clock epoch extension.

Most importantly, whenever we set the TOD, we have to update the CKC
timer.

Introduce "tcg_s390x.h" just like "kvm_s390x.h" for tcg specific
function declarations that should not go into cpu.h.

Reviewed-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: David Hildenbrand <david@redhat.com>
Message-Id: <20180627134410.4901-6-david@redhat.com>
Signed-off-by: Cornelia Huck <cohuck@redhat.com>
This commit is contained in:
David Hildenbrand 2018-06-27 15:44:06 +02:00 committed by Cornelia Huck
parent f777b20544
commit 7de3b1cdc6
8 changed files with 109 additions and 34 deletions

View File

@ -11,19 +11,43 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "hw/s390x/tod.h" #include "hw/s390x/tod.h"
#include "qemu/timer.h"
#include "qemu/cutils.h"
#include "cpu.h"
#include "tcg_s390x.h"
static void qemu_s390_tod_get(const S390TODState *td, S390TOD *tod, static void qemu_s390_tod_get(const S390TODState *td, S390TOD *tod,
Error **errp) Error **errp)
{ {
/* FIXME */ *tod = td->base;
tod->high = 0;
tod->low = 0; tod->low += time2tod(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
if (tod->low < td->base.low) {
tod->high++;
}
} }
static void qemu_s390_tod_set(S390TODState *td, const S390TOD *tod, static void qemu_s390_tod_set(S390TODState *td, const S390TOD *tod,
Error **errp) Error **errp)
{ {
/* FIXME */ CPUState *cpu;
td->base = *tod;
td->base.low -= time2tod(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
if (td->base.low > tod->low) {
td->base.high--;
}
/*
* The TOD has been changed and we have to recalculate the CKC values
* for all CPUs. We do this asynchronously, as "SET CLOCK should be
* issued only while all other activity on all CPUs .. has been
* suspended".
*/
CPU_FOREACH(cpu) {
async_run_on_cpu(cpu, tcg_s390_tod_updated, RUN_ON_CPU_NULL);
}
} }
static void qemu_s390_tod_class_init(ObjectClass *oc, void *data) static void qemu_s390_tod_class_init(ObjectClass *oc, void *data)
@ -34,10 +58,24 @@ static void qemu_s390_tod_class_init(ObjectClass *oc, void *data)
tdc->set = qemu_s390_tod_set; tdc->set = qemu_s390_tod_set;
} }
static void qemu_s390_tod_init(Object *obj)
{
S390TODState *td = S390_TOD(obj);
struct tm tm;
qemu_get_timedate(&tm, 0);
td->base.high = 0;
td->base.low = TOD_UNIX_EPOCH + (time2tod(mktimegm(&tm)) * 1000000000ULL);
if (td->base.low < TOD_UNIX_EPOCH) {
td->base.high += 1;
}
}
static TypeInfo qemu_s390_tod_info = { static TypeInfo qemu_s390_tod_info = {
.name = TYPE_QEMU_S390_TOD, .name = TYPE_QEMU_S390_TOD,
.parent = TYPE_S390_TOD, .parent = TYPE_S390_TOD,
.instance_size = sizeof(S390TODState), .instance_size = sizeof(S390TODState),
.instance_init = qemu_s390_tod_init,
.class_init = qemu_s390_tod_class_init, .class_init = qemu_s390_tod_class_init,
.class_size = sizeof(S390TODClass), .class_size = sizeof(S390TODClass),
}; };

View File

@ -30,6 +30,17 @@ void s390_init_tod(void)
qdev_init_nofail(DEVICE(obj)); qdev_init_nofail(DEVICE(obj));
} }
S390TODState *s390_get_todstate(void)
{
static S390TODState *ts;
if (!ts) {
ts = S390_TOD(object_resolve_path_type("", TYPE_S390_TOD, NULL));
}
return ts;
}
#define S390_TOD_CLOCK_VALUE_MISSING 0x00 #define S390_TOD_CLOCK_VALUE_MISSING 0x00
#define S390_TOD_CLOCK_VALUE_PRESENT 0x01 #define S390_TOD_CLOCK_VALUE_PRESENT 0x01

View File

@ -30,6 +30,9 @@ typedef struct S390TOD {
typedef struct S390TODState { typedef struct S390TODState {
/* private */ /* private */
DeviceState parent_obj; DeviceState parent_obj;
/* unused by KVM implementation */
S390TOD base;
} S390TODState; } S390TODState;
typedef struct S390TODClass { typedef struct S390TODClass {
@ -41,6 +44,22 @@ typedef struct S390TODClass {
void (*set)(S390TODState *td, const S390TOD *tod, Error **errp); void (*set)(S390TODState *td, const S390TOD *tod, Error **errp);
} S390TODClass; } S390TODClass;
/* The value of the TOD clock for 1.1.1970. */
#define TOD_UNIX_EPOCH 0x7d91048bca000000ULL
/* Converts ns to s390's clock format */
static inline uint64_t time2tod(uint64_t ns)
{
return (ns << 9) / 125 + (((ns & 0xff10000000000000ull) / 125) << 9);
}
/* Converts s390's clock format to ns */
static inline uint64_t tod2time(uint64_t t)
{
return ((t >> 9) * 125) + (((t & 0x1ff) * 125) >> 9);
}
void s390_init_tod(void); void s390_init_tod(void);
S390TODState *s390_get_todstate(void);
#endif #endif

View File

@ -30,7 +30,6 @@
#include "kvm_s390x.h" #include "kvm_s390x.h"
#include "sysemu/kvm.h" #include "sysemu/kvm.h"
#include "qemu-common.h" #include "qemu-common.h"
#include "qemu/cutils.h"
#include "qemu/timer.h" #include "qemu/timer.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "trace.h" #include "trace.h"
@ -275,9 +274,6 @@ static void s390_cpu_initfn(Object *obj)
CPUState *cs = CPU(obj); CPUState *cs = CPU(obj);
S390CPU *cpu = S390_CPU(obj); S390CPU *cpu = S390_CPU(obj);
CPUS390XState *env = &cpu->env; CPUS390XState *env = &cpu->env;
#if !defined(CONFIG_USER_ONLY)
struct tm tm;
#endif
cs->env_ptr = env; cs->env_ptr = env;
cs->halted = 1; cs->halted = 1;
@ -286,9 +282,6 @@ static void s390_cpu_initfn(Object *obj)
s390_cpu_get_crash_info_qom, NULL, NULL, NULL, NULL); s390_cpu_get_crash_info_qom, NULL, NULL, NULL, NULL);
s390_cpu_model_register_props(obj); s390_cpu_model_register_props(obj);
#if !defined(CONFIG_USER_ONLY) #if !defined(CONFIG_USER_ONLY)
qemu_get_timedate(&tm, 0);
env->tod_offset = TOD_UNIX_EPOCH +
(time2tod(mktimegm(&tm)) * 1000000000ULL);
env->tod_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_tod_timer, cpu); env->tod_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_tod_timer, cpu);
env->cpu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_cpu_timer, cpu); env->cpu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_cpu_timer, cpu);
s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu); s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu);

View File

@ -130,7 +130,6 @@ struct CPUS390XState {
uint64_t cpuid; uint64_t cpuid;
#endif #endif
uint64_t tod_offset;
QEMUTimer *tod_timer; QEMUTimer *tod_timer;
QEMUTimer *cpu_timer; QEMUTimer *cpu_timer;

View File

@ -237,22 +237,6 @@ enum cc_op {
CC_OP_MAX CC_OP_MAX
}; };
/* The value of the TOD clock for 1.1.1970. */
#define TOD_UNIX_EPOCH 0x7d91048bca000000ULL
/* Converts ns to s390's clock format */
static inline uint64_t time2tod(uint64_t ns)
{
return (ns << 9) / 125 + (((ns & 0xff10000000000000ull) / 125) << 9);
}
/* Converts s390's clock format to ns */
static inline uint64_t tod2time(uint64_t t)
{
return ((t >> 9) * 125) + (((t & 0x1ff) * 125) >> 9);
}
static inline hwaddr decode_basedisp_s(CPUS390XState *env, uint32_t ipb, static inline hwaddr decode_basedisp_s(CPUS390XState *env, uint32_t ipb,
uint8_t *ar) uint8_t *ar)
{ {

View File

@ -28,6 +28,8 @@
#include "qemu/timer.h" #include "qemu/timer.h"
#include "exec/exec-all.h" #include "exec/exec-all.h"
#include "exec/cpu_ldst.h" #include "exec/cpu_ldst.h"
#include "qapi/error.h"
#include "tcg_s390x.h"
#if !defined(CONFIG_USER_ONLY) #if !defined(CONFIG_USER_ONLY)
#include "sysemu/cpus.h" #include "sysemu/cpus.h"
@ -39,6 +41,7 @@
#include "hw/s390x/ioinst.h" #include "hw/s390x/ioinst.h"
#include "hw/s390x/s390-pci-inst.h" #include "hw/s390x/s390-pci-inst.h"
#include "hw/boards.h" #include "hw/boards.h"
#include "hw/s390x/tod.h"
#endif #endif
/* #define DEBUG_HELPER */ /* #define DEBUG_HELPER */
@ -138,17 +141,19 @@ void HELPER(spx)(CPUS390XState *env, uint64_t a1)
/* Store Clock */ /* Store Clock */
uint64_t HELPER(stck)(CPUS390XState *env) uint64_t HELPER(stck)(CPUS390XState *env)
{ {
uint64_t time; S390TODState *td = s390_get_todstate();
S390TODClass *tdc = S390_TOD_GET_CLASS(td);
S390TOD tod;
time = env->tod_offset + tdc->get(td, &tod, &error_abort);
time2tod(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); return tod.low;
return time;
} }
/* Set Clock Comparator */ /* Set Clock Comparator */
void HELPER(sckc)(CPUS390XState *env, uint64_t time) void HELPER(sckc)(CPUS390XState *env, uint64_t time)
{ {
S390TODState *td = s390_get_todstate();
if (time == -1ULL) { if (time == -1ULL) {
return; return;
} }
@ -156,7 +161,7 @@ void HELPER(sckc)(CPUS390XState *env, uint64_t time)
env->ckc = time; env->ckc = time;
/* difference between origins */ /* difference between origins */
time -= env->tod_offset; time -= td->base.low;
/* nanoseconds */ /* nanoseconds */
time = tod2time(time); time = tod2time(time);
@ -164,6 +169,14 @@ void HELPER(sckc)(CPUS390XState *env, uint64_t time)
timer_mod(env->tod_timer, time); timer_mod(env->tod_timer, time);
} }
void tcg_s390_tod_updated(CPUState *cs, run_on_cpu_data opaque)
{
S390CPU *cpu = S390_CPU(cs);
CPUS390XState *env = &cpu->env;
helper_sckc(env, env->ckc);
}
/* Set Tod Programmable Field */ /* Set Tod Programmable Field */
void HELPER(sckpf)(CPUS390XState *env, uint64_t r0) void HELPER(sckpf)(CPUS390XState *env, uint64_t r0)
{ {

18
target/s390x/tcg_s390x.h Normal file
View File

@ -0,0 +1,18 @@
/*
* QEMU TCG support -- s390x specific functions.
*
* Copyright 2018 Red Hat, Inc.
*
* Authors:
* David Hildenbrand <david@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef TCG_S390X_H
#define TCG_S390X_H
void tcg_s390_tod_updated(CPUState *cs, run_on_cpu_data opaque);
#endif /* TCG_S390X_H */