Commit 7de3b1cd authored by David Hildenbrand's avatar David Hildenbrand Committed by Cornelia Huck
Browse files

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: default avatarThomas Huth <thuth@redhat.com>
Signed-off-by: default avatarDavid Hildenbrand <david@redhat.com>
Message-Id: <20180627134410.4901-6-david@redhat.com>
Signed-off-by: default avatarCornelia Huck <cohuck@redhat.com>
parent f777b205
Loading
Loading
Loading
Loading
+42 −4
Original line number Diff line number Diff line
@@ -11,19 +11,43 @@
#include "qemu/osdep.h"
#include "qapi/error.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,
                              Error **errp)
{
    /* FIXME */
    tod->high = 0;
    tod->low = 0;
    *tod = td->base;

    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,
                              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)
@@ -34,10 +58,24 @@ static void qemu_s390_tod_class_init(ObjectClass *oc, void *data)
    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 = {
    .name = TYPE_QEMU_S390_TOD,
    .parent = TYPE_S390_TOD,
    .instance_size = sizeof(S390TODState),
    .instance_init = qemu_s390_tod_init,
    .class_init = qemu_s390_tod_class_init,
    .class_size = sizeof(S390TODClass),
};
+11 −0
Original line number Diff line number Diff line
@@ -30,6 +30,17 @@ void s390_init_tod(void)
    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_PRESENT    0x01

+19 −0
Original line number Diff line number Diff line
@@ -30,6 +30,9 @@ typedef struct S390TOD {
typedef struct S390TODState {
    /* private */
    DeviceState parent_obj;

    /* unused by KVM implementation */
    S390TOD base;
} S390TODState;

typedef struct S390TODClass {
@@ -41,6 +44,22 @@ typedef struct S390TODClass {
    void (*set)(S390TODState *td, const S390TOD *tod, Error **errp);
} 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);
S390TODState *s390_get_todstate(void);

#endif
+0 −7
Original line number Diff line number Diff line
@@ -30,7 +30,6 @@
#include "kvm_s390x.h"
#include "sysemu/kvm.h"
#include "qemu-common.h"
#include "qemu/cutils.h"
#include "qemu/timer.h"
#include "qemu/error-report.h"
#include "trace.h"
@@ -275,9 +274,6 @@ static void s390_cpu_initfn(Object *obj)
    CPUState *cs = CPU(obj);
    S390CPU *cpu = S390_CPU(obj);
    CPUS390XState *env = &cpu->env;
#if !defined(CONFIG_USER_ONLY)
    struct tm tm;
#endif

    cs->env_ptr = env;
    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_model_register_props(obj);
#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->cpu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_cpu_timer, cpu);
    s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu);
+0 −1
Original line number Diff line number Diff line
@@ -130,7 +130,6 @@ struct CPUS390XState {
    uint64_t cpuid;
#endif

    uint64_t tod_offset;
    QEMUTimer *tod_timer;

    QEMUTimer *cpu_timer;
Loading