diff mbox series

mc146818rtc: add a way to generate RTC interrupts via QMP

Message ID 20240425133745.464091-1-d-tatianin@yandex-team.ru
State New
Headers show
Series mc146818rtc: add a way to generate RTC interrupts via QMP | expand

Commit Message

Daniil Tatianin April 25, 2024, 1:37 p.m. UTC
This can be used to force-synchronize the time in guest after a long
stop-cont pause, which can be useful for serverless-type workload.

Signed-off-by: Daniil Tatianin <d-tatianin@yandex-team.ru>
---
 hw/rtc/mc146818rtc.c         | 15 +++++++++++++++
 include/hw/rtc/mc146818rtc.h |  1 +
 qapi/misc-target.json        | 16 ++++++++++++++++
 3 files changed, 32 insertions(+)

Comments

Markus Armbruster April 26, 2024, 8:39 a.m. UTC | #1
Daniil Tatianin <d-tatianin@yandex-team.ru> writes:

> This can be used to force-synchronize the time in guest after a long
> stop-cont pause, which can be useful for serverless-type workload.
>
> Signed-off-by: Daniil Tatianin <d-tatianin@yandex-team.ru>
> ---
>  hw/rtc/mc146818rtc.c         | 15 +++++++++++++++
>  include/hw/rtc/mc146818rtc.h |  1 +
>  qapi/misc-target.json        | 16 ++++++++++++++++
>  3 files changed, 32 insertions(+)
>
> diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c
> index f4c1869232..6980a78d5f 100644
> --- a/hw/rtc/mc146818rtc.c
> +++ b/hw/rtc/mc146818rtc.c
> @@ -116,6 +116,21 @@ void qmp_rtc_reset_reinjection(Error **errp)
>      }
>  }
>  
> +void qmp_rtc_notify(Error **errp)
> +{
> +    MC146818RtcState *s;
> +
> +    /*
> +     * See:
> +     * https://www.kernel.org/doc/Documentation/virtual/kvm/timekeeping.txt
> +     */
> +    QLIST_FOREACH(s, &rtc_devices, link) {
> +        s->cmos_data[RTC_REG_B] |= REG_B_UIE;
> +        s->cmos_data[RTC_REG_C] |= REG_C_IRQF | REG_C_UF;
> +        qemu_irq_raise(s->irq);
> +    }
> +}
> +

Note for later: qmp_rtc_notify() works on all realized mc146818rtc
devices.  Other kinds of RTC devices are silently ignored.  Just like
qmp_rtc_reset_reinjection().

>  static bool rtc_policy_slew_deliver_irq(MC146818RtcState *s)
>  {
>      kvm_reset_irq_delivered();
> diff --git a/include/hw/rtc/mc146818rtc.h b/include/hw/rtc/mc146818rtc.h
> index 97cec0b3e8..5229dffbbd 100644
> --- a/include/hw/rtc/mc146818rtc.h
> +++ b/include/hw/rtc/mc146818rtc.h
> @@ -56,5 +56,6 @@ MC146818RtcState *mc146818_rtc_init(ISABus *bus, int base_year,
>  void mc146818rtc_set_cmos_data(MC146818RtcState *s, int addr, int val);
>  int mc146818rtc_get_cmos_data(MC146818RtcState *s, int addr);
>  void qmp_rtc_reset_reinjection(Error **errp);
> +void qmp_rtc_notify(Error **errp);
>  
>  #endif /* HW_RTC_MC146818RTC_H */
> diff --git a/qapi/misc-target.json b/qapi/misc-target.json
> index 4e0a6492a9..20457b0acc 100644
> --- a/qapi/misc-target.json
> +++ b/qapi/misc-target.json
> @@ -19,6 +19,22 @@
   ##
   # @rtc-reset-reinjection:
   #
   # This command will reset the RTC interrupt reinjection backlog.  Can
   # be used if another mechanism to synchronize guest time is in effect,
   # for example QEMU guest agent's guest-set-time command.
   #
   # Since: 2.1
   #
   # Example:
   #
   #     -> { "execute": "rtc-reset-reinjection" }
   #     <- { "return": {} }
   ##
>  { 'command': 'rtc-reset-reinjection',
>    'if': 'TARGET_I386' }
>  
> +##
> +# @rtc-notify:
> +#
> +# Generate an RTC interrupt.

Our QMP command to generate NMIs is called inject-nmi.  Call this one
inject-rtc-irq for consistency?  rtc-inject-irq?

> +#
> +# Since: 9.1
> +#
> +# Example:
> +#
> +#     -> { "execute": "rtc-notify" }
> +#     <- { "return": {} }
> +#
> +##
> +{ 'command': 'rtc-notify',
> +  'if': 'TARGET_I386' }
> +

As noted above, both commands silently ignore RTCs other than
mc146818rtc.

They're only available with TARGET_I386.

As long as all machines available with TARGET_I386 can only ever contain
mc146818rtc RTCs, ignoring other RTCs is a non-problem in practice.

Feels a bit fragile to me.  Thoughts?

>  ##
>  # @SevState:
>  #
Daniil Tatianin April 26, 2024, 9:28 a.m. UTC | #2
On 4/26/24 11:39 AM, Markus Armbruster wrote:

> Daniil Tatianin <d-tatianin@yandex-team.ru> writes:
>
>> This can be used to force-synchronize the time in guest after a long
>> stop-cont pause, which can be useful for serverless-type workload.
>>
>> Signed-off-by: Daniil Tatianin <d-tatianin@yandex-team.ru>
>> ---
>>   hw/rtc/mc146818rtc.c         | 15 +++++++++++++++
>>   include/hw/rtc/mc146818rtc.h |  1 +
>>   qapi/misc-target.json        | 16 ++++++++++++++++
>>   3 files changed, 32 insertions(+)
>>
>> diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c
>> index f4c1869232..6980a78d5f 100644
>> --- a/hw/rtc/mc146818rtc.c
>> +++ b/hw/rtc/mc146818rtc.c
>> @@ -116,6 +116,21 @@ void qmp_rtc_reset_reinjection(Error **errp)
>>       }
>>   }
>>   
>> +void qmp_rtc_notify(Error **errp)
>> +{
>> +    MC146818RtcState *s;
>> +
>> +    /*
>> +     * See:
>> +     * https://www.kernel.org/doc/Documentation/virtual/kvm/timekeeping.txt
>> +     */
>> +    QLIST_FOREACH(s, &rtc_devices, link) {
>> +        s->cmos_data[RTC_REG_B] |= REG_B_UIE;
>> +        s->cmos_data[RTC_REG_C] |= REG_C_IRQF | REG_C_UF;
>> +        qemu_irq_raise(s->irq);
>> +    }
>> +}
>> +
> Note for later: qmp_rtc_notify() works on all realized mc146818rtc
> devices.  Other kinds of RTC devices are silently ignored.  Just like
> qmp_rtc_reset_reinjection().
>
>>   static bool rtc_policy_slew_deliver_irq(MC146818RtcState *s)
>>   {
>>       kvm_reset_irq_delivered();
>> diff --git a/include/hw/rtc/mc146818rtc.h b/include/hw/rtc/mc146818rtc.h
>> index 97cec0b3e8..5229dffbbd 100644
>> --- a/include/hw/rtc/mc146818rtc.h
>> +++ b/include/hw/rtc/mc146818rtc.h
>> @@ -56,5 +56,6 @@ MC146818RtcState *mc146818_rtc_init(ISABus *bus, int base_year,
>>   void mc146818rtc_set_cmos_data(MC146818RtcState *s, int addr, int val);
>>   int mc146818rtc_get_cmos_data(MC146818RtcState *s, int addr);
>>   void qmp_rtc_reset_reinjection(Error **errp);
>> +void qmp_rtc_notify(Error **errp);
>>   
>>   #endif /* HW_RTC_MC146818RTC_H */
>> diff --git a/qapi/misc-target.json b/qapi/misc-target.json
>> index 4e0a6492a9..20457b0acc 100644
>> --- a/qapi/misc-target.json
>> +++ b/qapi/misc-target.json
>> @@ -19,6 +19,22 @@
>     ##
>     # @rtc-reset-reinjection:
>     #
>     # This command will reset the RTC interrupt reinjection backlog.  Can
>     # be used if another mechanism to synchronize guest time is in effect,
>     # for example QEMU guest agent's guest-set-time command.
>     #
>     # Since: 2.1
>     #
>     # Example:
>     #
>     #     -> { "execute": "rtc-reset-reinjection" }
>     #     <- { "return": {} }
>     ##
>>   { 'command': 'rtc-reset-reinjection',
>>     'if': 'TARGET_I386' }
>>   
>> +##
>> +# @rtc-notify:
>> +#
>> +# Generate an RTC interrupt.
> Our QMP command to generate NMIs is called inject-nmi.  Call this one
> inject-rtc-irq for consistency?  rtc-inject-irq?

This makes sense, I'll rename in the next version. Thanks.

>> +#
>> +# Since: 9.1
>> +#
>> +# Example:
>> +#
>> +#     -> { "execute": "rtc-notify" }
>> +#     <- { "return": {} }
>> +#
>> +##
>> +{ 'command': 'rtc-notify',
>> +  'if': 'TARGET_I386' }
>> +
> As noted above, both commands silently ignore RTCs other than
> mc146818rtc.
>
> They're only available with TARGET_I386.
>
> As long as all machines available with TARGET_I386 can only ever contain
> mc146818rtc RTCs, ignoring other RTCs is a non-problem in practice.
>
> Feels a bit fragile to me.  Thoughts?

Feels a bit fragile indeed, but this code has been there since 2.1, and 
I guess no one really found this to be a problem.

>>   ##
>>   # @SevState:
>>   #
Markus Armbruster April 26, 2024, 9:47 a.m. UTC | #3
Daniil Tatianin <d-tatianin@yandex-team.ru> writes:

> On 4/26/24 11:39 AM, Markus Armbruster wrote:
>
>> Daniil Tatianin <d-tatianin@yandex-team.ru> writes:
>>
>>> This can be used to force-synchronize the time in guest after a long
>>> stop-cont pause, which can be useful for serverless-type workload.
>>>
>>> Signed-off-by: Daniil Tatianin <d-tatianin@yandex-team.ru>
>>> ---
>>>   hw/rtc/mc146818rtc.c         | 15 +++++++++++++++
>>>   include/hw/rtc/mc146818rtc.h |  1 +
>>>   qapi/misc-target.json        | 16 ++++++++++++++++
>>>   3 files changed, 32 insertions(+)
>>>
>>> diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c
>>> index f4c1869232..6980a78d5f 100644
>>> --- a/hw/rtc/mc146818rtc.c
>>> +++ b/hw/rtc/mc146818rtc.c
>>> @@ -116,6 +116,21 @@ void qmp_rtc_reset_reinjection(Error **errp)
>>>       }
>>>   }
>>>   
>>> +void qmp_rtc_notify(Error **errp)
>>> +{
>>> +    MC146818RtcState *s;
>>> +
>>> +    /*
>>> +     * See:
>>> +     * https://www.kernel.org/doc/Documentation/virtual/kvm/timekeeping.txt
>>> +     */
>>> +    QLIST_FOREACH(s, &rtc_devices, link) {
>>> +        s->cmos_data[RTC_REG_B] |= REG_B_UIE;
>>> +        s->cmos_data[RTC_REG_C] |= REG_C_IRQF | REG_C_UF;
>>> +        qemu_irq_raise(s->irq);
>>> +    }
>>> +}
>>> +
>>
>> Note for later: qmp_rtc_notify() works on all realized mc146818rtc
>> devices.  Other kinds of RTC devices are silently ignored.  Just like
>> qmp_rtc_reset_reinjection().
>>
>>>   static bool rtc_policy_slew_deliver_irq(MC146818RtcState *s)
>>>   {
>>>       kvm_reset_irq_delivered();
>>> diff --git a/include/hw/rtc/mc146818rtc.h b/include/hw/rtc/mc146818rtc.h
>>> index 97cec0b3e8..5229dffbbd 100644
>>> --- a/include/hw/rtc/mc146818rtc.h
>>> +++ b/include/hw/rtc/mc146818rtc.h
>>> @@ -56,5 +56,6 @@ MC146818RtcState *mc146818_rtc_init(ISABus *bus, int base_year,
>>>   void mc146818rtc_set_cmos_data(MC146818RtcState *s, int addr, int val);
>>>   int mc146818rtc_get_cmos_data(MC146818RtcState *s, int addr);
>>>   void qmp_rtc_reset_reinjection(Error **errp);
>>> +void qmp_rtc_notify(Error **errp);
>>>   
>>>   #endif /* HW_RTC_MC146818RTC_H */
>>> diff --git a/qapi/misc-target.json b/qapi/misc-target.json
>>> index 4e0a6492a9..20457b0acc 100644
>>> --- a/qapi/misc-target.json
>>> +++ b/qapi/misc-target.json
>>> @@ -19,6 +19,22 @@
>>     ##
>>     # @rtc-reset-reinjection:
>>     #
>>     # This command will reset the RTC interrupt reinjection backlog.  Can
>>     # be used if another mechanism to synchronize guest time is in effect,
>>     # for example QEMU guest agent's guest-set-time command.
>>     #
>>     # Since: 2.1
>>     #
>>     # Example:
>>     #
>>     #     -> { "execute": "rtc-reset-reinjection" }
>>     #     <- { "return": {} }
>>     ##
>>>   { 'command': 'rtc-reset-reinjection',
>>>     'if': 'TARGET_I386' }
>>>   
>>> +##
>>> +# @rtc-notify:
>>> +#
>>> +# Generate an RTC interrupt.
>>
>> Our QMP command to generate NMIs is called inject-nmi.  Call this one
>> inject-rtc-irq for consistency?  rtc-inject-irq?
>
> This makes sense, I'll rename in the next version. Thanks.
>
>>> +#
>>> +# Since: 9.1
>>> +#
>>> +# Example:
>>> +#
>>> +#     -> { "execute": "rtc-notify" }
>>> +#     <- { "return": {} }
>>> +#
>>> +##
>>> +{ 'command': 'rtc-notify',
>>> +  'if': 'TARGET_I386' }
>>> +
>>
>> As noted above, both commands silently ignore RTCs other than
>> mc146818rtc.
>>
>> They're only available with TARGET_I386.
>>
>> As long as all machines available with TARGET_I386 can only ever contain
>> mc146818rtc RTCs, ignoring other RTCs is a non-problem in practice.
>>
>> Feels a bit fragile to me.  Thoughts?
>
> Feels a bit fragile indeed, but this code has been there since 2.1, and 
> I guess no one really found this to be a problem.

Needs a comment at least.  I'd put it before the two fragile functions
in mc146818rtc.c.

Even better would be to make the build fail if TARGET_I386 picks up
another RTC, but I don't know how.

>>>   ##
>>>   # @SevState:
>>>   #
Philippe Mathieu-Daudé April 26, 2024, 9:48 a.m. UTC | #4
Hi Daniil, Markus,

On 26/4/24 10:39, Markus Armbruster wrote:
> Daniil Tatianin <d-tatianin@yandex-team.ru> writes:
> 
>> This can be used to force-synchronize the time in guest after a long
>> stop-cont pause, which can be useful for serverless-type workload.
>>
>> Signed-off-by: Daniil Tatianin <d-tatianin@yandex-team.ru>
>> ---
>>   hw/rtc/mc146818rtc.c         | 15 +++++++++++++++
>>   include/hw/rtc/mc146818rtc.h |  1 +
>>   qapi/misc-target.json        | 16 ++++++++++++++++
>>   3 files changed, 32 insertions(+)
>>
>> diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c
>> index f4c1869232..6980a78d5f 100644
>> --- a/hw/rtc/mc146818rtc.c
>> +++ b/hw/rtc/mc146818rtc.c
>> @@ -116,6 +116,21 @@ void qmp_rtc_reset_reinjection(Error **errp)
>>       }
>>   }
>>   
>> +void qmp_rtc_notify(Error **errp)
>> +{
>> +    MC146818RtcState *s;
>> +
>> +    /*
>> +     * See:
>> +     * https://www.kernel.org/doc/Documentation/virtual/kvm/timekeeping.txt
>> +     */
>> +    QLIST_FOREACH(s, &rtc_devices, link) {
>> +        s->cmos_data[RTC_REG_B] |= REG_B_UIE;
>> +        s->cmos_data[RTC_REG_C] |= REG_C_IRQF | REG_C_UF;
>> +        qemu_irq_raise(s->irq);
>> +    }
>> +}
>> +
> 
> Note for later: qmp_rtc_notify() works on all realized mc146818rtc
> devices.  Other kinds of RTC devices are silently ignored.  Just like
> qmp_rtc_reset_reinjection().

IMO to avoid any future ambiguity (in heterogeneous machines), this
command must take a QOM device path (or a list of) and only notify
those.

Regards,

Phil.
Philippe Mathieu-Daudé April 29, 2024, 9:43 a.m. UTC | #5
On 26/4/24 11:48, Philippe Mathieu-Daudé wrote:
> Hi Daniil, Markus,
> 
> On 26/4/24 10:39, Markus Armbruster wrote:
>> Daniil Tatianin <d-tatianin@yandex-team.ru> writes:
>>
>>> This can be used to force-synchronize the time in guest after a long
>>> stop-cont pause, which can be useful for serverless-type workload.
>>>
>>> Signed-off-by: Daniil Tatianin <d-tatianin@yandex-team.ru>
>>> ---
>>>   hw/rtc/mc146818rtc.c         | 15 +++++++++++++++
>>>   include/hw/rtc/mc146818rtc.h |  1 +
>>>   qapi/misc-target.json        | 16 ++++++++++++++++
>>>   3 files changed, 32 insertions(+)
>>>
>>> diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c
>>> index f4c1869232..6980a78d5f 100644
>>> --- a/hw/rtc/mc146818rtc.c
>>> +++ b/hw/rtc/mc146818rtc.c
>>> @@ -116,6 +116,21 @@ void qmp_rtc_reset_reinjection(Error **errp)
>>>       }
>>>   }
>>> +void qmp_rtc_notify(Error **errp)
>>> +{
>>> +    MC146818RtcState *s;
>>> +
>>> +    /*
>>> +     * See:
>>> +     * 
>>> https://www.kernel.org/doc/Documentation/virtual/kvm/timekeeping.txt
>>> +     */
>>> +    QLIST_FOREACH(s, &rtc_devices, link) {
>>> +        s->cmos_data[RTC_REG_B] |= REG_B_UIE;
>>> +        s->cmos_data[RTC_REG_C] |= REG_C_IRQF | REG_C_UF;
>>> +        qemu_irq_raise(s->irq);
>>> +    }
>>> +}
>>> +
>>
>> Note for later: qmp_rtc_notify() works on all realized mc146818rtc
>> devices.  Other kinds of RTC devices are silently ignored.  Just like
>> qmp_rtc_reset_reinjection().
> 
> IMO to avoid any future ambiguity (in heterogeneous machines), this
> command must take a QOM device path (or a list of) and only notify
> those.

If you disagree, at least please rename the command/method using
"broadcast" for trailer.
Markus Armbruster April 29, 2024, 11:32 a.m. UTC | #6
Philippe Mathieu-Daudé <philmd@linaro.org> writes:

> Hi Daniil, Markus,
>
> On 26/4/24 10:39, Markus Armbruster wrote:
>> Daniil Tatianin <d-tatianin@yandex-team.ru> writes:
>> 
>>> This can be used to force-synchronize the time in guest after a long
>>> stop-cont pause, which can be useful for serverless-type workload.
>>>
>>> Signed-off-by: Daniil Tatianin <d-tatianin@yandex-team.ru>
>>> ---
>>>   hw/rtc/mc146818rtc.c         | 15 +++++++++++++++
>>>   include/hw/rtc/mc146818rtc.h |  1 +
>>>   qapi/misc-target.json        | 16 ++++++++++++++++
>>>   3 files changed, 32 insertions(+)
>>>
>>> diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c
>>> index f4c1869232..6980a78d5f 100644
>>> --- a/hw/rtc/mc146818rtc.c
>>> +++ b/hw/rtc/mc146818rtc.c
>>> @@ -116,6 +116,21 @@ void qmp_rtc_reset_reinjection(Error **errp)
>>>       }
>>>   }
>>>   +void qmp_rtc_notify(Error **errp)
>>> +{
>>> +    MC146818RtcState *s;
>>> +
>>> +    /*
>>> +     * See:
>>> +     * https://www.kernel.org/doc/Documentation/virtual/kvm/timekeeping.txt
>>> +     */
>>> +    QLIST_FOREACH(s, &rtc_devices, link) {
>>> +        s->cmos_data[RTC_REG_B] |= REG_B_UIE;
>>> +        s->cmos_data[RTC_REG_C] |= REG_C_IRQF | REG_C_UF;
>>> +        qemu_irq_raise(s->irq);
>>> +    }
>>> +}
>>> +
>> Note for later: qmp_rtc_notify() works on all realized mc146818rtc
>> devices.  Other kinds of RTC devices are silently ignored.  Just like
>> qmp_rtc_reset_reinjection().
>
> IMO to avoid any future ambiguity (in heterogeneous machines), this
> command must take a QOM device path (or a list of) and only notify
> those.

Let's compare:

• With QOM path:

  · You need to know the machine's RTC device(s).

    Unfortunately, this is bothersome, as the QOM path is not stable.

    For Q35, it's generally "/machine/unattached/device[N]/rtc", but N
    varies with configuration (TCG N=2, KVM N=3 for me), and it might
    vary with machine type version.  That's because the machine code
    creates ICH9-LPC without a proper name.  We do that a lot.  I hate
    it.

    Likewise for i440FX with PIIX3 instead of ICH9-LPC.

    For isapc, it's /machine/unattached/device[3].  I suspect the 3
    isn't reliable there, either.

    microvm doesn't seem to have an RTC by default.

  · If the device so named doesn't support IRQ inject, the command
    should fail.

  · Could be generalized to non-RTC devices when that's useful.

• Broadcast:

  · You don't need to know the machine's RTC device(s).

  · If there are multiple RTC devices that support IRQ inject, we inject
    for each of them.  There is no way to select specific RTCs.

  · If there is no RTC device that supports IRQ inject, the command does
    nothing silently.

    I don't like silent failures.  It could be made to fail instead.

If it wasn't for the unstable QOM path problem, I'd advise against
the broadcast interface.

Thoughts?
Philippe Mathieu-Daudé April 29, 2024, 1:39 p.m. UTC | #7
(+Peter who has more experience on such design).

On 29/4/24 13:32, Markus Armbruster wrote:
> Philippe Mathieu-Daudé <philmd@linaro.org> writes:
> 
>> Hi Daniil, Markus,
>>
>> On 26/4/24 10:39, Markus Armbruster wrote:
>>> Daniil Tatianin <d-tatianin@yandex-team.ru> writes:
>>>
>>>> This can be used to force-synchronize the time in guest after a long
>>>> stop-cont pause, which can be useful for serverless-type workload.

What is a "serverless-type workload"?

>>>> Signed-off-by: Daniil Tatianin <d-tatianin@yandex-team.ru>
>>>> ---
>>>>    hw/rtc/mc146818rtc.c         | 15 +++++++++++++++
>>>>    include/hw/rtc/mc146818rtc.h |  1 +
>>>>    qapi/misc-target.json        | 16 ++++++++++++++++
>>>>    3 files changed, 32 insertions(+)
>>>>
>>>> diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c
>>>> index f4c1869232..6980a78d5f 100644
>>>> --- a/hw/rtc/mc146818rtc.c
>>>> +++ b/hw/rtc/mc146818rtc.c
>>>> @@ -116,6 +116,21 @@ void qmp_rtc_reset_reinjection(Error **errp)
>>>>        }
>>>>    }
>>>>    +void qmp_rtc_notify(Error **errp)
>>>> +{
>>>> +    MC146818RtcState *s;
>>>> +
>>>> +    /*
>>>> +     * See:
>>>> +     * https://www.kernel.org/doc/Documentation/virtual/kvm/timekeeping.txt

What part of this document explains why this change is required?
I probably missed it. Explaining it here briefly would be more
useful.

>>>> +     */
>>>> +    QLIST_FOREACH(s, &rtc_devices, link) {
>>>> +        s->cmos_data[RTC_REG_B] |= REG_B_UIE;
                                       // Update-ended interrupt enable

>>>> +        s->cmos_data[RTC_REG_C] |= REG_C_IRQF | REG_C_UF;
                                       // interrupt request flag
                                       //           update interrupt flag

>>>> +        qemu_irq_raise(s->irq);
>>>> +    }
>>>> +}
>>>> +
>>> Note for later: qmp_rtc_notify() works on all realized mc146818rtc
>>> devices.  Other kinds of RTC devices are silently ignored.  Just like
>>> qmp_rtc_reset_reinjection().
>>
>> IMO to avoid any future ambiguity (in heterogeneous machines), this
>> command must take a QOM device path (or a list of) and only notify
>> those.
> 
> Let's compare:
> 
> • With QOM path:
> 
>    · You need to know the machine's RTC device(s).
> 
>      Unfortunately, this is bothersome, as the QOM path is not stable.

But we'll need more of that with dynamic machines...

>      For Q35, it's generally "/machine/unattached/device[N]/rtc", but N
>      varies with configuration (TCG N=2, KVM N=3 for me), and it might
>      vary with machine type version.  That's because the machine code
>      creates ICH9-LPC without a proper name.  We do that a lot.  I hate
>      it.
> 
>      Likewise for i440FX with PIIX3 instead of ICH9-LPC.
> 
>      For isapc, it's /machine/unattached/device[3].  I suspect the 3
>      isn't reliable there, either.
> 
>      microvm doesn't seem to have an RTC by default.
> 
>    · If the device so named doesn't support IRQ inject, the command
>      should fail.

Yes, why the management app would want to run this command if there
are not RTC on the machine?

>    · Could be generalized to non-RTC devices when that's useful.
> 
> • Broadcast:
> 
>    · You don't need to know the machine's RTC device(s).
> 
>    · If there are multiple RTC devices that support IRQ inject, we inject
>      for each of them.  There is no way to select specific RTCs.
> 
>    · If there is no RTC device that supports IRQ inject, the command does
>      nothing silently.
> 
>      I don't like silent failures.  It could be made to fail instead.
> 
> If it wasn't for the unstable QOM path problem, I'd advise against
> the broadcast interface.
> 
> Thoughts?

Something bugs me in this patch but I couldn't figure out what I am
missing. The issue is when migrated VM is restored. I don't get why
the behavior depends on an external decision (via external management
transport). Don't we have post_load() hooks for such tuning?
This device implements it in rtc_post_load().

Regards,

Phil.

PD: BTW tomorrow community call could be a good opportunity to discuss
this.
Markus Armbruster April 29, 2024, 2:02 p.m. UTC | #8
Philippe Mathieu-Daudé <philmd@linaro.org> writes:

> (+Peter who has more experience on such design).
>
> On 29/4/24 13:32, Markus Armbruster wrote:
>> Philippe Mathieu-Daudé <philmd@linaro.org> writes:

[...]

>>> IMO to avoid any future ambiguity (in heterogeneous machines), this
>>> command must take a QOM device path (or a list of) and only notify
>>> those.
>> 
>> Let's compare:
>> 
>> • With QOM path:
>> 
>>    · You need to know the machine's RTC device(s).
>> 
>>      Unfortunately, this is bothersome, as the QOM path is not stable.
>
> But we'll need more of that with dynamic machines...

I view /machine/unattached a technical debt (see "hate" right below).

It saved us the trouble of coming up with sensible names for onboard
devices.

And now the interest is about to be due.

>>      For Q35, it's generally "/machine/unattached/device[N]/rtc", but N
>>      varies with configuration (TCG N=2, KVM N=3 for me), and it might
>>      vary with machine type version.  That's because the machine code
>>      creates ICH9-LPC without a proper name.  We do that a lot.  I hate
>>      it.
>> 
>>      Likewise for i440FX with PIIX3 instead of ICH9-LPC.
>> 
>>      For isapc, it's /machine/unattached/device[3].  I suspect the 3
>>      isn't reliable there, either.
>> 
>>      microvm doesn't seem to have an RTC by default.

[...]
Daniil Tatianin May 2, 2024, 7:30 a.m. UTC | #9
On 4/29/24 4:39 PM, Philippe Mathieu-Daudé wrote:

> (+Peter who has more experience on such design).
>
> On 29/4/24 13:32, Markus Armbruster wrote:
>> Philippe Mathieu-Daudé <philmd@linaro.org> writes:
>>
>>> Hi Daniil, Markus,
>>>
>>> On 26/4/24 10:39, Markus Armbruster wrote:
>>>> Daniil Tatianin <d-tatianin@yandex-team.ru> writes:
>>>>
>>>>> This can be used to force-synchronize the time in guest after a long
>>>>> stop-cont pause, which can be useful for serverless-type workload.
>
> What is a "serverless-type workload"?

That's when your VM instance only runs on demand for a few seconds 
before going to sleep again
until the next user request comes in.

>>>>> Signed-off-by: Daniil Tatianin <d-tatianin@yandex-team.ru>
>>>>> ---
>>>>>    hw/rtc/mc146818rtc.c         | 15 +++++++++++++++
>>>>>    include/hw/rtc/mc146818rtc.h |  1 +
>>>>>    qapi/misc-target.json        | 16 ++++++++++++++++
>>>>>    3 files changed, 32 insertions(+)
>>>>>
>>>>> diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c
>>>>> index f4c1869232..6980a78d5f 100644
>>>>> --- a/hw/rtc/mc146818rtc.c
>>>>> +++ b/hw/rtc/mc146818rtc.c
>>>>> @@ -116,6 +116,21 @@ void qmp_rtc_reset_reinjection(Error **errp)
>>>>>        }
>>>>>    }
>>>>>    +void qmp_rtc_notify(Error **errp)
>>>>> +{
>>>>> +    MC146818RtcState *s;
>>>>> +
>>>>> +    /*
>>>>> +     * See:
>>>>> +     * 
>>>>> https://www.kernel.org/doc/Documentation/virtual/kvm/timekeeping.txt
>
> What part of this document explains why this change is required?
> I probably missed it. Explaining it here briefly would be more
> useful.

Sure, will do!

>
>>>>> +     */
>>>>> +    QLIST_FOREACH(s, &rtc_devices, link) {
>>>>> +        s->cmos_data[RTC_REG_B] |= REG_B_UIE;
>                                       // Update-ended interrupt enable
>
>>>>> +        s->cmos_data[RTC_REG_C] |= REG_C_IRQF | REG_C_UF;
>                                       // interrupt request flag
>                                       //           update interrupt flag
>
>>>>> +        qemu_irq_raise(s->irq);
>>>>> +    }
>>>>> +}
>>>>> +
>>>> Note for later: qmp_rtc_notify() works on all realized mc146818rtc
>>>> devices.  Other kinds of RTC devices are silently ignored. Just like
>>>> qmp_rtc_reset_reinjection().
>>>
>>> IMO to avoid any future ambiguity (in heterogeneous machines), this
>>> command must take a QOM device path (or a list of) and only notify
>>> those.
>>
>> Let's compare:
>>
>> • With QOM path:
>>
>>    · You need to know the machine's RTC device(s).
>>
>>      Unfortunately, this is bothersome, as the QOM path is not stable.
>
> But we'll need more of that with dynamic machines...
>
>>      For Q35, it's generally "/machine/unattached/device[N]/rtc", but N
>>      varies with configuration (TCG N=2, KVM N=3 for me), and it might
>>      vary with machine type version.  That's because the machine code
>>      creates ICH9-LPC without a proper name.  We do that a lot. I hate
>>      it.
>>
>>      Likewise for i440FX with PIIX3 instead of ICH9-LPC.
>>
>>      For isapc, it's /machine/unattached/device[3].  I suspect the 3
>>      isn't reliable there, either.
>>
>>      microvm doesn't seem to have an RTC by default.
>>
>>    · If the device so named doesn't support IRQ inject, the command
>>      should fail.
>
> Yes, why the management app would want to run this command if there
> are not RTC on the machine?
>
>>    · Could be generalized to non-RTC devices when that's useful.
>>
>> • Broadcast:
>>
>>    · You don't need to know the machine's RTC device(s).
>>
>>    · If there are multiple RTC devices that support IRQ inject, we 
>> inject
>>      for each of them.  There is no way to select specific RTCs.
>>
>>    · If there is no RTC device that supports IRQ inject, the command 
>> does
>>      nothing silently.
>>
>>      I don't like silent failures.  It could be made to fail instead.
>>
>> If it wasn't for the unstable QOM path problem, I'd advise against
>> the broadcast interface.
>>
>> Thoughts?
>
> Something bugs me in this patch but I couldn't figure out what I am
> missing. The issue is when migrated VM is restored. I don't get why
> the behavior depends on an external decision (via external management
> transport). Don't we have post_load() hooks for such tuning?
> This device implements it in rtc_post_load().

This isn't really related to migration though. Serverless is based on 
constantly stopping and resuming the VM on e.g. every HTTP request to an 
endpoint.

As far as Markus' comment goes, I propose we go the broadcast route, and 
just add the -broadcast suffix to the current command name.

If everyone is okay with that, I will submit a v3.

Thank you!

>
> Regards,
>
> Phil.
>
> PD: BTW tomorrow community call could be a good opportunity to discuss
> this.
>
diff mbox series

Patch

diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c
index f4c1869232..6980a78d5f 100644
--- a/hw/rtc/mc146818rtc.c
+++ b/hw/rtc/mc146818rtc.c
@@ -116,6 +116,21 @@  void qmp_rtc_reset_reinjection(Error **errp)
     }
 }
 
+void qmp_rtc_notify(Error **errp)
+{
+    MC146818RtcState *s;
+
+    /*
+     * See:
+     * https://www.kernel.org/doc/Documentation/virtual/kvm/timekeeping.txt
+     */
+    QLIST_FOREACH(s, &rtc_devices, link) {
+        s->cmos_data[RTC_REG_B] |= REG_B_UIE;
+        s->cmos_data[RTC_REG_C] |= REG_C_IRQF | REG_C_UF;
+        qemu_irq_raise(s->irq);
+    }
+}
+
 static bool rtc_policy_slew_deliver_irq(MC146818RtcState *s)
 {
     kvm_reset_irq_delivered();
diff --git a/include/hw/rtc/mc146818rtc.h b/include/hw/rtc/mc146818rtc.h
index 97cec0b3e8..5229dffbbd 100644
--- a/include/hw/rtc/mc146818rtc.h
+++ b/include/hw/rtc/mc146818rtc.h
@@ -56,5 +56,6 @@  MC146818RtcState *mc146818_rtc_init(ISABus *bus, int base_year,
 void mc146818rtc_set_cmos_data(MC146818RtcState *s, int addr, int val);
 int mc146818rtc_get_cmos_data(MC146818RtcState *s, int addr);
 void qmp_rtc_reset_reinjection(Error **errp);
+void qmp_rtc_notify(Error **errp);
 
 #endif /* HW_RTC_MC146818RTC_H */
diff --git a/qapi/misc-target.json b/qapi/misc-target.json
index 4e0a6492a9..20457b0acc 100644
--- a/qapi/misc-target.json
+++ b/qapi/misc-target.json
@@ -19,6 +19,22 @@ 
 { 'command': 'rtc-reset-reinjection',
   'if': 'TARGET_I386' }
 
+##
+# @rtc-notify:
+#
+# Generate an RTC interrupt.
+#
+# Since: 9.1
+#
+# Example:
+#
+#     -> { "execute": "rtc-notify" }
+#     <- { "return": {} }
+#
+##
+{ 'command': 'rtc-notify',
+  'if': 'TARGET_I386' }
+
 ##
 # @SevState:
 #