qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH v4 01/10] hw/mips/cputimer: Don't start periodic


From: Paolo Bonzini
Subject: Re: [Qemu-devel] [PATCH v4 01/10] hw/mips/cputimer: Don't start periodic timer in KVM mode
Date: Thu, 20 Mar 2014 23:36:27 +0100
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20100101 Thunderbird/24.2.0

Il 20/03/2014 10:57, James Hogan ha scritto:
> On 19/03/14 16:29, Paolo Bonzini wrote:
>> Il 14/03/2014 13:47, James Hogan ha scritto:
>>> From: Sanjay Lal <address@hidden>
>>>
>>> Compare/Count timer interrupts are handled in-kernel for KVM, so don't
>>> bother starting it in QEMU.
>>>
>>> Signed-off-by: Sanjay Lal <address@hidden>
>>> Signed-off-by: James Hogan <address@hidden>
>>> Reviewed-by: Aurelien Jarno <address@hidden>
>>> ---
>>> Changes in v2:
>>>  - Expand commit message
>>>  - Rebase on v1.7.0
>>>  - Wrap comment
>>> ---
>>>  hw/mips/cputimer.c | 13 ++++++++++---
>>>  1 file changed, 10 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/hw/mips/cputimer.c b/hw/mips/cputimer.c
>>> index c8b4b00..52570fd 100644
>>> --- a/hw/mips/cputimer.c
>>> +++ b/hw/mips/cputimer.c
>>> @@ -23,6 +23,7 @@
>>>  #include "hw/hw.h"
>>>  #include "hw/mips/cpudevs.h"
>>>  #include "qemu/timer.h"
>>> +#include "sysemu/kvm.h"
>>>
>>>  #define TIMER_FREQ    100 * 1000 * 1000
>>>
>>> @@ -141,7 +142,13 @@ static void mips_timer_cb (void *opaque)
>>>
>>>  void cpu_mips_clock_init (CPUMIPSState *env)
>>>  {
>>> -    env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &mips_timer_cb, env);
>>> -    env->CP0_Compare = 0;
>>> -    cpu_mips_store_count(env, 1);
>>> +    /*
>>> +     * If we're in KVM mode, don't start the periodic timer, that is
>>> handled in
>>> +     * kernel.
>>> +     */
>>> +    if (!kvm_enabled()) {
>>> +        env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &mips_timer_cb,
>>> env);
>>> +        env->CP0_Compare = 0;
>>> +        cpu_mips_store_count(env, 1);
>>> +    }
>>>  }
>>>
>>
>> I hate to make you do unrelated changes, but... initializing CP0_Compare
>> is unnecessary, it should already be 0;
> 
> You mean because of the memset in object_initialize_with_type, when
> object_new is called? Although that wouldn't handle reset, although
> technically the reset state of Compare is undefined.

No, see mips_cpu_reset:

static void mips_cpu_reset(CPUState *s)
{
    MIPSCPU *cpu = MIPS_CPU(s);
    MIPSCPUClass *mcc = MIPS_CPU_GET_CLASS(cpu);
    CPUMIPSState *env = &cpu->env;

    mcc->parent_reset(s);

    memset(env, 0, offsetof(CPUMIPSState, mvp));
    tlb_flush(s, 1);

    cpu_state_reset(env);
}

Fields before mvp are reset to zero (including CP0_Compare and CP0_Count).

> Am I right that the correct way to prevent clock drift is for
> kvm_arch_put_registers to only set the Count register if level !=
> KVM_PUT_RUNTIME_STATE?

Yes, that makes sense.  Or, better, do not provide a set_onereg 
interface for CP0_Count.  Instead, in the kernel you can base the CPU 
timer on the value of CLOCK_MONOTONIC, like this:

+static inline u64 get_monotonic_ns(void)
+{
+       struct timespec ts;
+
+       ktime_get_ts(&ts);
+       return timespec_to_ns(&ts);
+}
+

Then you provide three set_onereg interfaces.  One is normal cp0_count, 
but it is only used if the timer is not running (according to 
cp0_cause).  The second is the rate at which the timer counts 
(cp0_count_hz).  The third is used when the timer is running, and
it is:

        cp0_count_bias
           = cp0_count * 10^9 / cp0_count_hz - get_monotonic_ns()

So when the timer is running cp0_count is computed as follows:

        cp0_count =
          = (get_monotonic_ns() + cp0_count_bias) * cp0_count_hz / 10^9

QEMU can then set:

  cp0_count = cpu_mips_get_count(env)
  cp0_count_bias =
     cpu_mips_get_count(env) * 10^9 / cp0_count_hz - qemu_get_clock_ns(rt_clock)

Note that QEMU's qemu_get_clock_ns(rt_clock) == kernel's get_monotonic_ns().

So when the guest reads cp0_count (and the timer was running at the time
kvm_arch_put_registers was set), the kernel will return:

        cp0_count =
         = (get_monotonic_ns() + cp0_count_bias) * cp0_count_hz / 10^9
         = env->cp0_count
           + (get_monotonic_ns() - qemu_get_clock_ns(rt_clock)
                                 + qemu_get_clock_ns(vm_clock)) * cp0_count_hz 
/ 10^9
         = env->cp0_count + qemu_get_clock_ns(vm_clock) * cp0_count_hz / 10^9
         = cpu_mips_get_count(env)
        
Paolo



reply via email to

[Prev in Thread] Current Thread [Next in Thread]