qemu-devel
[Top][All Lists]
Advanced

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

Re: [RFC v1 34/38] target/arm: cpu: only initialize TCG gt timers under


From: Claudio Fontana
Subject: Re: [RFC v1 34/38] target/arm: cpu: only initialize TCG gt timers under CONFIG_TCG
Date: Tue, 23 Feb 2021 13:38:54 +0100
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.12.0

On 2/23/21 12:36 PM, Claudio Fontana wrote:
> On 2/23/21 12:01 PM, Alex Bennée wrote:
>>
>> Claudio Fontana <cfontana@suse.de> writes:
>>
>>> On 2/22/21 4:34 PM, Alex Bennée wrote:
>>>>
>>>> Claudio Fontana <cfontana@suse.de> writes:
>>>>
>>>>> From: Claudio Fontana <cfontana@centriq4.arch.suse.de>
>>>>>
>>>>> KVM has its own cpu->kvm_vtime.
>>>>>
>>>>> Adjust cpu vmstate by putting unused fields instead of the
>>>>> VMSTATE_TIMER_PTR when TCG is not available.
>>>>>
>>>>> Signed-off-by: Claudio Fontana <cfontana@suse.de>
>>>>> ---
>>>>>  target/arm/cpu.c     | 4 +++-
>>>>>  target/arm/machine.c | 5 +++++
>>>>>  2 files changed, 8 insertions(+), 1 deletion(-)
>>>>>
>>>>> diff --git a/target/arm/cpu.c b/target/arm/cpu.c
>>>>> index 1d81a1e7ac..b929109054 100644
>>>>> --- a/target/arm/cpu.c
>>>>> +++ b/target/arm/cpu.c
>>>>> @@ -1322,6 +1322,7 @@ static void arm_cpu_realizefn(DeviceState *dev, 
>>>>> Error **errp)
>>>>>          }
>>>>>      }
>>>>>  
>>>>> +#ifdef CONFIG_TCG
>>>>>      {
>>>>>          uint64_t scale;
>>>>>  
>>>>> @@ -1347,7 +1348,8 @@ static void arm_cpu_realizefn(DeviceState *dev, 
>>>>> Error **errp)
>>>>>          cpu->gt_timer[GTIMER_HYPVIRT] = timer_new(QEMU_CLOCK_VIRTUAL, 
>>>>> scale,
>>>>>                                                    arm_gt_hvtimer_cb, 
>>>>> cpu);
>>>>>      }
>>>>> -#endif
>>>>> +#endif /* CONFIG_TCG */
>>>>> +#endif /* !CONFIG_USER_ONLY */
>>>>>  
>>>>>      cpu_exec_realizefn(cs, &local_err);
>>>>>      if (local_err != NULL) {
>>>>> diff --git a/target/arm/machine.c b/target/arm/machine.c
>>>>> index 666ef329ef..13d7c6d930 100644
>>>>> --- a/target/arm/machine.c
>>>>> +++ b/target/arm/machine.c
>>>>> @@ -822,8 +822,13 @@ const VMStateDescription vmstate_arm_cpu = {
>>>>>          VMSTATE_UINT32(env.exception.syndrome, ARMCPU),
>>>>>          VMSTATE_UINT32(env.exception.fsr, ARMCPU),
>>>>>          VMSTATE_UINT64(env.exception.vaddress, ARMCPU),
>>>>> +#ifdef CONFIG_TCG
>>>>>          VMSTATE_TIMER_PTR(gt_timer[GTIMER_PHYS], ARMCPU),
>>>>>          VMSTATE_TIMER_PTR(gt_timer[GTIMER_VIRT], ARMCPU),
>>>>> +#else
>>>>> +        VMSTATE_UNUSED(sizeof(QEMUTimer *)),
>>>>> +        VMSTATE_UNUSED(sizeof(QEMUTimer *)),
>>>>> +#endif /* CONFIG_TCG */
>>>>
>>>> I'm not sure this is correct - VMSTATE_TIMER_PTR chases the links to
>>>> just expose expired time but QEMUTimer has more in it than that. Paolo
>>>
>>>
>>> I am not sure I follow can you state more precisely where the issue could 
>>> be?
>>>
>>> it's not a VMSTATE_TIMER, it's a VMSTATE_TIMER_PTR,
>>> it ends up in VMSTATE_POINTER where a single pointer is assigned;
>>
>> Does it? I thought it ended up with the .expire_time (int64_t) which
>> will be bigger than sizeof(QemuTimer *) on a 32 bit system.
> 
> Ok I understand what you mean. Lets see:
> 
> Looking at vmstate.h,
> 
> #define VMSTATE_TIMER_PTR(_f, _s)                                         \
>     VMSTATE_TIMER_PTR_V(_f, _s, 0)
> 
> #define VMSTATE_TIMER_PTR_V(_f, _s, _v)                                   \
>     VMSTATE_POINTER(_f, _s, _v, vmstate_info_timer, QEMUTimer *)
> 
> #define VMSTATE_POINTER(_field, _state, _version, _info, _type) {    \
>     .name       = (stringify(_field)),                               \
>     .version_id = (_version),                                        \
>     .info       = &(_info),                                          \
>     .size       = sizeof(_type),                                     \
>     .flags      = VMS_SINGLE|VMS_POINTER,                            \
>     .offset     = vmstate_offset_value(_state, _field, _type),       \
> }
> 
> so here we get the vmstate field definition.
> 
> .size is fine, as it is sizeof(QEMUTimer *).
> 
> .info, is &vmstate_info_timer, migration/savevm.c:
> 
> const VMStateInfo vmstate_info_timer = {
>     .name = "timer",
>     .get  = get_timer,
>     .put  = put_timer,
> };
> 
> void timer_put(QEMUFile *f, QEMUTimer *ts)
> {
>     uint64_t expire_time;
> 
>     expire_time = timer_expire_time_ns(ts);
>     qemu_put_be64(f, expire_time);
> }
> 
> void timer_get(QEMUFile *f, QEMUTimer *ts)
> {
>     uint64_t expire_time;
> 
>     expire_time = qemu_get_be64(f);
>     if (expire_time != -1) {
>         timer_mod_ns(ts, expire_time);
>     } else {
>         timer_del(ts);
>     }
> }
> 
> ---
> 
> And the migration code does: (migration/vmstate.c):
> 
> int vmstate_save_state_v() {
>   ...
>   ret = field->info->put(f, curr_elem, size, field, vmdesc_loop);
>   ...
> }
> 
> which puts a BE64 in the QEMUFile *f (see timer_put above).
> 
> The load code in the same file does:
> 
> int vmstate_load_state() {
>   ...
>   ret = field->info->get(f, curr_elem, size, field);
>   ...
> }
> 
> which reads a BE64 from the QEMUFile *f (see timer_get above).
> 
> Would be "fine" from the field sizes perspective (the .size of the field 
> type, and the value of the BE64),
> 
> but it's the calculations done in timer_get and timer_put which are scary, as 
> they dereference the timer pointer.
> 
> 
> Should we actually have a check for null pointer in vmstate.c?
> 
> We _do_ have one in vmstate_save_state_v and vmstate_load_state, but it is 
> actually active only for VMS_ARRAY_OF_POINTER.
> Why? Why not also do the same (write the null pointer and not following it) 
> for normal VMS_POINTER ?
> 
> int vmstate_save_state_v() {
>  ...
>                 if (!curr_elem && size) {
>                     /* if null pointer write placeholder and do not follow */
>                     assert(field->flags & VMS_ARRAY_OF_POINTER);
>                     ret = vmstate_info_nullptr.put(f, curr_elem, size, NULL,
>                                                    NULL);
> 
>  ...
> 
> 
> int vmstate_load_state() {
> 
> ...
>                 if (!curr_elem && size) {
>                     /* if null pointer check placeholder and do not follow */
>                     assert(field->flags & VMS_ARRAY_OF_POINTER);
>                     ret = vmstate_info_nullptr.get(f, curr_elem, size, NULL);
> ...
> 
> }
> 
> 
> This is worthwhile investigating further, any other ideas?
> 
> Thanks,
> 
> Claudio
> 
> 

Btw here it would be good to be able to rely on the existing tests,
do we have full coverage of these compatibility situations?

According to make check it's all a-ok, but... is the testing coverage 
insufficient
for these VMSTATE compatibility issues?

Ciao,

Claudio


>>
>>>
>>> so if we don't use gt_timer at all (as is the case with !CONFIG_TCG), we 
>>> just
>>> need to ensure that an unused number is there to assign, migrating from old 
>>> to new version?
>>>
>>>
>>>> suggested a straight VMSTATE_UNUSED(8) on IRC but I wonder if it would
>>>> be better to have a VMSTATE_UNUSED_TIMER?
>>>>
>>>> I don't think there is an impact for Xen because I'm fairly certain
>>>> migration isn't a thing we do - but I'll double check.
>>>>
>>>
>>> Thanks Alex, that would be helpful,
>>> if Xen uses gt_timer in any way I would not want to unwillingly break
>>> it.
>>
>> Not for ARM no, currently there is no ARM specific machine emulated by
>> QEMU for Xen. All ARM guests are PV guests.
>>
>>>
>>> Thanks,
>>>
>>> Claudio
>>
>>
> 




reply via email to

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