diff mbox series

[08/19] aspeed/timer: Fix behaviour running Linux

Message ID 20190525151241.5017-9-clg@kaod.org
State Superseded
Headers show
Series None | expand

Commit Message

Cédric Le Goater May 25, 2019, 3:12 p.m. UTC
From: Joel Stanley <joel@jms.id.au>


The Linux kernel driver was updated in commit 4451d3f59f2a
("clocksource/drivers/fttmr010: Fix set_next_event handler) to fix an
issue observed on hardware:

 > RELOAD register is loaded into COUNT register when the aspeed timer

 > is enabled, which means the next event may be delayed because timer

 > interrupt won't be generated until <0xFFFFFFFF - current_count +

 > cycles>.


When running under Qemu, the system appeared "laggy". The guest is now
scheduling timer events too regularly, starving the host of CPU time.

This patch modifies the timer model to attempt to schedule the timer
expiry as the guest requests, but if we have missed the deadline we
re interrupt and try again, which allows the guest to catch up.

Provides expected behaviour with old and new guest code.

Fixes: c04bd47db6b9 ("hw/timer: Add ASPEED timer device model")
Signed-off-by: Joel Stanley <joel@jms.id.au>

[clg: - merged a fix from Andrew Jeffery <andrew@aj.id.au>
        "Fire interrupt on failure to meet deadline"
        https://lists.ozlabs.org/pipermail/openbmc/2019-January/014641.html
      - adapted commit log
      - checkpatch fixes ]
Signed-off-by: Cédric Le Goater <clg@kaod.org>

---
 hw/timer/aspeed_timer.c | 59 ++++++++++++++++++++++-------------------
 1 file changed, 31 insertions(+), 28 deletions(-)

-- 
2.20.1

Comments

Cédric Le Goater Sept. 22, 2023, 1:21 p.m. UTC | #1
Joel, Andrew,

On 5/25/19 17:12, Cédric Le Goater wrote:
> From: Joel Stanley <joel@jms.id.au>
> 
> The Linux kernel driver was updated in commit 4451d3f59f2a
> ("clocksource/drivers/fttmr010: Fix set_next_event handler) to fix an
> issue observed on hardware:
> 
>   > RELOAD register is loaded into COUNT register when the aspeed timer
>   > is enabled, which means the next event may be delayed because timer
>   > interrupt won't be generated until <0xFFFFFFFF - current_count +
>   > cycles>.
> 
> When running under Qemu, the system appeared "laggy". The guest is now
> scheduling timer events too regularly, starving the host of CPU time.
> 
> This patch modifies the timer model to attempt to schedule the timer
> expiry as the guest requests, but if we have missed the deadline we
> re interrupt and try again, which allows the guest to catch up.
> 
> Provides expected behaviour with old and new guest code.
> 
> Fixes: c04bd47db6b9 ("hw/timer: Add ASPEED timer device model")
> Signed-off-by: Joel Stanley <joel@jms.id.au>
> [clg: - merged a fix from Andrew Jeffery <andrew@aj.id.au>
>          "Fire interrupt on failure to meet deadline"
>          https://lists.ozlabs.org/pipermail/openbmc/2019-January/014641.html
>        - adapted commit log
>        - checkpatch fixes ]
> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> ---
>   hw/timer/aspeed_timer.c | 59 ++++++++++++++++++++++-------------------
>   1 file changed, 31 insertions(+), 28 deletions(-)
> 
> diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c
> index 5c786e512815..9ffd8e09f670 100644
> --- a/hw/timer/aspeed_timer.c
> +++ b/hw/timer/aspeed_timer.c
> @@ -109,37 +109,40 @@ static inline uint64_t calculate_time(struct AspeedTimer *t, uint32_t ticks)
>   
>   static uint64_t calculate_next(struct AspeedTimer *t)
>   {
> -    uint64_t next = 0;
> -    uint32_t rate = calculate_rate(t);
> +    uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +    uint64_t next;
>   
> -    while (!next) {
> -        /* We don't know the relationship between the values in the match
> -         * registers, so sort using MAX/MIN/zero. We sort in that order as the
> -         * timer counts down to zero. */
> -        uint64_t seq[] = {
> -            calculate_time(t, MAX(t->match[0], t->match[1])),
> -            calculate_time(t, MIN(t->match[0], t->match[1])),
> -            calculate_time(t, 0),
> -        };
> -        uint64_t reload_ns;
> -        uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> -
> -        if (now < seq[0]) {
> -            next = seq[0];
> -        } else if (now < seq[1]) {
> -            next = seq[1];
> -        } else if (now < seq[2]) {
> -            next = seq[2];
> -        } else if (t->reload) {
> -            reload_ns = muldiv64(t->reload, NANOSECONDS_PER_SECOND, rate);
> -            t->start = now - ((now - t->start) % reload_ns);
> -        } else {
> -            /* no reload value, return 0 */
> -            break;
> -        }
> +    /*
> +     * We don't know the relationship between the values in the match
> +     * registers, so sort using MAX/MIN/zero. We sort in that order as
> +     * the timer counts down to zero.
> +     */
> +
> +    next = calculate_time(t, MAX(t->match[0], t->match[1]));
> +    if (now < next) {
> +        return next;
> +    }
> +
> +    next = calculate_time(t, MIN(t->match[0], t->match[1]));
> +    if (now < next) {
> +        return next;
> +    }
> +
> +    next = calculate_time(t, 0);
> +    if (now < next) {
> +        return next;
> +    }
> +
> +    /* We've missed all deadlines, fire interrupt and try again */
> +    timer_del(&t->timer);
> +
> +    if (timer_overflow_interrupt(t)) {
> +        t->level = !t->level;
> +        qemu_set_irq(t->irq, t->level);
>       }
>   
> -    return next;
> +    t->start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +    return calculate_time(t, MAX(MAX(t->match[0], t->match[1]), 0));

This MAX(MAX(x, y), 0) looks strange to me. Would you remember where it comes
from ? Thanks,

C.


>   }
>   
>   static void aspeed_timer_mod(AspeedTimer *t)
Andrew Jeffery Sept. 25, 2023, 7:54 a.m. UTC | #2
On Fri, 22 Sep 2023, at 22:51, Cédric Le Goater wrote:
> Joel, Andrew,
>
> On 5/25/19 17:12, Cédric Le Goater wrote:
>> From: Joel Stanley <joel@jms.id.au>
>> 
>> The Linux kernel driver was updated in commit 4451d3f59f2a
>> ("clocksource/drivers/fttmr010: Fix set_next_event handler) to fix an
>> issue observed on hardware:
>> 
>>   > RELOAD register is loaded into COUNT register when the aspeed timer
>>   > is enabled, which means the next event may be delayed because timer
>>   > interrupt won't be generated until <0xFFFFFFFF - current_count +
>>   > cycles>.
>> 
>> When running under Qemu, the system appeared "laggy". The guest is now
>> scheduling timer events too regularly, starving the host of CPU time.
>> 
>> This patch modifies the timer model to attempt to schedule the timer
>> expiry as the guest requests, but if we have missed the deadline we
>> re interrupt and try again, which allows the guest to catch up.
>> 
>> Provides expected behaviour with old and new guest code.
>> 
>> Fixes: c04bd47db6b9 ("hw/timer: Add ASPEED timer device model")
>> Signed-off-by: Joel Stanley <joel@jms.id.au>
>> [clg: - merged a fix from Andrew Jeffery <andrew@aj.id.au>
>>          "Fire interrupt on failure to meet deadline"
>>          https://lists.ozlabs.org/pipermail/openbmc/2019-January/014641.html
>>        - adapted commit log
>>        - checkpatch fixes ]
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
>> ---
>>   hw/timer/aspeed_timer.c | 59 ++++++++++++++++++++++-------------------
>>   1 file changed, 31 insertions(+), 28 deletions(-)
>> 
>> diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c
>> index 5c786e512815..9ffd8e09f670 100644
>> --- a/hw/timer/aspeed_timer.c
>> +++ b/hw/timer/aspeed_timer.c
>> @@ -109,37 +109,40 @@ static inline uint64_t calculate_time(struct AspeedTimer *t, uint32_t ticks)
>>   
>>   static uint64_t calculate_next(struct AspeedTimer *t)
>>   {
>> -    uint64_t next = 0;
>> -    uint32_t rate = calculate_rate(t);
>> +    uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
>> +    uint64_t next;
>>   
>> -    while (!next) {
>> -        /* We don't know the relationship between the values in the match
>> -         * registers, so sort using MAX/MIN/zero. We sort in that order as the
>> -         * timer counts down to zero. */
>> -        uint64_t seq[] = {
>> -            calculate_time(t, MAX(t->match[0], t->match[1])),
>> -            calculate_time(t, MIN(t->match[0], t->match[1])),
>> -            calculate_time(t, 0),
>> -        };
>> -        uint64_t reload_ns;
>> -        uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
>> -
>> -        if (now < seq[0]) {
>> -            next = seq[0];
>> -        } else if (now < seq[1]) {
>> -            next = seq[1];
>> -        } else if (now < seq[2]) {
>> -            next = seq[2];
>> -        } else if (t->reload) {
>> -            reload_ns = muldiv64(t->reload, NANOSECONDS_PER_SECOND, rate);
>> -            t->start = now - ((now - t->start) % reload_ns);
>> -        } else {
>> -            /* no reload value, return 0 */
>> -            break;
>> -        }
>> +    /*
>> +     * We don't know the relationship between the values in the match
>> +     * registers, so sort using MAX/MIN/zero. We sort in that order as
>> +     * the timer counts down to zero.
>> +     */
>> +
>> +    next = calculate_time(t, MAX(t->match[0], t->match[1]));
>> +    if (now < next) {
>> +        return next;
>> +    }
>> +
>> +    next = calculate_time(t, MIN(t->match[0], t->match[1]));
>> +    if (now < next) {
>> +        return next;
>> +    }
>> +
>> +    next = calculate_time(t, 0);
>> +    if (now < next) {
>> +        return next;
>> +    }
>> +
>> +    /* We've missed all deadlines, fire interrupt and try again */
>> +    timer_del(&t->timer);
>> +
>> +    if (timer_overflow_interrupt(t)) {
>> +        t->level = !t->level;
>> +        qemu_set_irq(t->irq, t->level);
>>       }
>>   
>> -    return next;
>> +    t->start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
>> +    return calculate_time(t, MAX(MAX(t->match[0], t->match[1]), 0));
>
> This MAX(MAX(x, y), 0) looks strange to me. Would you remember where it comes
> from ? Thanks,

The inner MAX() deals with the lack of ordering constraints between the match values. I think the outer MAX() is redundant. We should probably remove it. The match member is type uint32_t so it can't be negative. You did steal that from an RFC patch :D

Andrew
Cédric Le Goater Sept. 25, 2023, 9:20 a.m. UTC | #3
On 9/25/23 09:54, Andrew Jeffery wrote:
> 
> 
> On Fri, 22 Sep 2023, at 22:51, Cédric Le Goater wrote:
>> Joel, Andrew,
>>
>> On 5/25/19 17:12, Cédric Le Goater wrote:
>>> From: Joel Stanley <joel@jms.id.au>
>>>
>>> The Linux kernel driver was updated in commit 4451d3f59f2a
>>> ("clocksource/drivers/fttmr010: Fix set_next_event handler) to fix an
>>> issue observed on hardware:
>>>
>>>    > RELOAD register is loaded into COUNT register when the aspeed timer
>>>    > is enabled, which means the next event may be delayed because timer
>>>    > interrupt won't be generated until <0xFFFFFFFF - current_count +
>>>    > cycles>.
>>>
>>> When running under Qemu, the system appeared "laggy". The guest is now
>>> scheduling timer events too regularly, starving the host of CPU time.
>>>
>>> This patch modifies the timer model to attempt to schedule the timer
>>> expiry as the guest requests, but if we have missed the deadline we
>>> re interrupt and try again, which allows the guest to catch up.
>>>
>>> Provides expected behaviour with old and new guest code.
>>>
>>> Fixes: c04bd47db6b9 ("hw/timer: Add ASPEED timer device model")
>>> Signed-off-by: Joel Stanley <joel@jms.id.au>
>>> [clg: - merged a fix from Andrew Jeffery <andrew@aj.id.au>
>>>           "Fire interrupt on failure to meet deadline"
>>>           https://lists.ozlabs.org/pipermail/openbmc/2019-January/014641.html
>>>         - adapted commit log
>>>         - checkpatch fixes ]
>>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
>>> ---
>>>    hw/timer/aspeed_timer.c | 59 ++++++++++++++++++++++-------------------
>>>    1 file changed, 31 insertions(+), 28 deletions(-)
>>>
>>> diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c
>>> index 5c786e512815..9ffd8e09f670 100644
>>> --- a/hw/timer/aspeed_timer.c
>>> +++ b/hw/timer/aspeed_timer.c
>>> @@ -109,37 +109,40 @@ static inline uint64_t calculate_time(struct AspeedTimer *t, uint32_t ticks)
>>>    
>>>    static uint64_t calculate_next(struct AspeedTimer *t)
>>>    {
>>> -    uint64_t next = 0;
>>> -    uint32_t rate = calculate_rate(t);
>>> +    uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
>>> +    uint64_t next;
>>>    
>>> -    while (!next) {
>>> -        /* We don't know the relationship between the values in the match
>>> -         * registers, so sort using MAX/MIN/zero. We sort in that order as the
>>> -         * timer counts down to zero. */
>>> -        uint64_t seq[] = {
>>> -            calculate_time(t, MAX(t->match[0], t->match[1])),
>>> -            calculate_time(t, MIN(t->match[0], t->match[1])),
>>> -            calculate_time(t, 0),
>>> -        };
>>> -        uint64_t reload_ns;
>>> -        uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
>>> -
>>> -        if (now < seq[0]) {
>>> -            next = seq[0];
>>> -        } else if (now < seq[1]) {
>>> -            next = seq[1];
>>> -        } else if (now < seq[2]) {
>>> -            next = seq[2];
>>> -        } else if (t->reload) {
>>> -            reload_ns = muldiv64(t->reload, NANOSECONDS_PER_SECOND, rate);
>>> -            t->start = now - ((now - t->start) % reload_ns);
>>> -        } else {
>>> -            /* no reload value, return 0 */
>>> -            break;
>>> -        }
>>> +    /*
>>> +     * We don't know the relationship between the values in the match
>>> +     * registers, so sort using MAX/MIN/zero. We sort in that order as
>>> +     * the timer counts down to zero.
>>> +     */
>>> +
>>> +    next = calculate_time(t, MAX(t->match[0], t->match[1]));
>>> +    if (now < next) {
>>> +        return next;
>>> +    }
>>> +
>>> +    next = calculate_time(t, MIN(t->match[0], t->match[1]));
>>> +    if (now < next) {
>>> +        return next;
>>> +    }
>>> +
>>> +    next = calculate_time(t, 0);
>>> +    if (now < next) {
>>> +        return next;
>>> +    }
>>> +
>>> +    /* We've missed all deadlines, fire interrupt and try again */
>>> +    timer_del(&t->timer);
>>> +
>>> +    if (timer_overflow_interrupt(t)) {
>>> +        t->level = !t->level;
>>> +        qemu_set_irq(t->irq, t->level);
>>>        }
>>>    
>>> -    return next;
>>> +    t->start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
>>> +    return calculate_time(t, MAX(MAX(t->match[0], t->match[1]), 0));
>>
>> This MAX(MAX(x, y), 0) looks strange to me. Would you remember where it comes
>> from ? Thanks,
> 
> The inner MAX() deals with the lack of ordering constraints between the match values. I think the outer MAX() is redundant. We should probably remove it. The match member is type uint32_t so it can't be negative. You did steal that from an RFC patch :D

I did ! Fixed there :

   https://patchwork.ozlabs.org/project/qemu-devel/patch/20230922155924.1172019-5-clg@kaod.org/

Cheers,

C.
Andrew Jeffery Sept. 26, 2023, 1:37 a.m. UTC | #4
On Mon, 25 Sep 2023, at 18:50, Cédric Le Goater wrote:
> On 9/25/23 09:54, Andrew Jeffery wrote:
>> 
>> 
>> On Fri, 22 Sep 2023, at 22:51, Cédric Le Goater wrote:
>>> Joel, Andrew,
>>>
>>> On 5/25/19 17:12, Cédric Le Goater wrote:
>>>> From: Joel Stanley <joel@jms.id.au>
>>>>
>>>> The Linux kernel driver was updated in commit 4451d3f59f2a
>>>> ("clocksource/drivers/fttmr010: Fix set_next_event handler) to fix an
>>>> issue observed on hardware:
>>>>
>>>>    > RELOAD register is loaded into COUNT register when the aspeed timer
>>>>    > is enabled, which means the next event may be delayed because timer
>>>>    > interrupt won't be generated until <0xFFFFFFFF - current_count +
>>>>    > cycles>.
>>>>
>>>> When running under Qemu, the system appeared "laggy". The guest is now
>>>> scheduling timer events too regularly, starving the host of CPU time.
>>>>
>>>> This patch modifies the timer model to attempt to schedule the timer
>>>> expiry as the guest requests, but if we have missed the deadline we
>>>> re interrupt and try again, which allows the guest to catch up.
>>>>
>>>> Provides expected behaviour with old and new guest code.
>>>>
>>>> Fixes: c04bd47db6b9 ("hw/timer: Add ASPEED timer device model")
>>>> Signed-off-by: Joel Stanley <joel@jms.id.au>
>>>> [clg: - merged a fix from Andrew Jeffery <andrew@aj.id.au>
>>>>           "Fire interrupt on failure to meet deadline"
>>>>           https://lists.ozlabs.org/pipermail/openbmc/2019-January/014641.html
>>>>         - adapted commit log
>>>>         - checkpatch fixes ]
>>>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
>>>> ---
>>>>    hw/timer/aspeed_timer.c | 59 ++++++++++++++++++++++-------------------
>>>>    1 file changed, 31 insertions(+), 28 deletions(-)
>>>>
>>>> diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c
>>>> index 5c786e512815..9ffd8e09f670 100644
>>>> --- a/hw/timer/aspeed_timer.c
>>>> +++ b/hw/timer/aspeed_timer.c
>>>> @@ -109,37 +109,40 @@ static inline uint64_t calculate_time(struct AspeedTimer *t, uint32_t ticks)
>>>>    
>>>>    static uint64_t calculate_next(struct AspeedTimer *t)
>>>>    {
>>>> -    uint64_t next = 0;
>>>> -    uint32_t rate = calculate_rate(t);
>>>> +    uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
>>>> +    uint64_t next;
>>>>    
>>>> -    while (!next) {
>>>> -        /* We don't know the relationship between the values in the match
>>>> -         * registers, so sort using MAX/MIN/zero. We sort in that order as the
>>>> -         * timer counts down to zero. */
>>>> -        uint64_t seq[] = {
>>>> -            calculate_time(t, MAX(t->match[0], t->match[1])),
>>>> -            calculate_time(t, MIN(t->match[0], t->match[1])),
>>>> -            calculate_time(t, 0),
>>>> -        };
>>>> -        uint64_t reload_ns;
>>>> -        uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
>>>> -
>>>> -        if (now < seq[0]) {
>>>> -            next = seq[0];
>>>> -        } else if (now < seq[1]) {
>>>> -            next = seq[1];
>>>> -        } else if (now < seq[2]) {
>>>> -            next = seq[2];
>>>> -        } else if (t->reload) {
>>>> -            reload_ns = muldiv64(t->reload, NANOSECONDS_PER_SECOND, rate);
>>>> -            t->start = now - ((now - t->start) % reload_ns);
>>>> -        } else {
>>>> -            /* no reload value, return 0 */
>>>> -            break;
>>>> -        }
>>>> +    /*
>>>> +     * We don't know the relationship between the values in the match
>>>> +     * registers, so sort using MAX/MIN/zero. We sort in that order as
>>>> +     * the timer counts down to zero.
>>>> +     */
>>>> +
>>>> +    next = calculate_time(t, MAX(t->match[0], t->match[1]));
>>>> +    if (now < next) {
>>>> +        return next;
>>>> +    }
>>>> +
>>>> +    next = calculate_time(t, MIN(t->match[0], t->match[1]));
>>>> +    if (now < next) {
>>>> +        return next;
>>>> +    }
>>>> +
>>>> +    next = calculate_time(t, 0);
>>>> +    if (now < next) {
>>>> +        return next;
>>>> +    }
>>>> +
>>>> +    /* We've missed all deadlines, fire interrupt and try again */
>>>> +    timer_del(&t->timer);
>>>> +
>>>> +    if (timer_overflow_interrupt(t)) {
>>>> +        t->level = !t->level;
>>>> +        qemu_set_irq(t->irq, t->level);
>>>>        }
>>>>    
>>>> -    return next;
>>>> +    t->start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
>>>> +    return calculate_time(t, MAX(MAX(t->match[0], t->match[1]), 0));
>>>
>>> This MAX(MAX(x, y), 0) looks strange to me. Would you remember where it comes
>>> from ? Thanks,
>> 
>> The inner MAX() deals with the lack of ordering constraints between the match values. I think the outer MAX() is redundant. We should probably remove it. The match member is type uint32_t so it can't be negative. You did steal that from an RFC patch :D
>
> I did ! Fixed there :
>
>    
> https://patchwork.ozlabs.org/project/qemu-devel/patch/20230922155924.1172019-5-clg@kaod.org/
>

Thanks. That one might be further down in my review queue 😅

Andrew
Joel Stanley Sept. 27, 2023, 2:12 a.m. UTC | #5
On Fri, 22 Sept 2023 at 13:21, Cédric Le Goater <clg@kaod.org> wrote:

> > +    t->start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> > +    return calculate_time(t, MAX(MAX(t->match[0], t->match[1]), 0));
>
> This MAX(MAX(x, y), 0) looks strange to me. Would you remember where it comes
> from ? Thanks,

That looks very strange. I think you've sorted it, so I wanted to
bring up the MAX macros themselves. It's unfortunate that they create
a non-unique local variable. Are we allowed to borrow the kernels
macros? They have some infrastructure for creating a unique local
variable name, as well as handling the const and non-const variants
with the one macro.

https://elixir.bootlin.com/linux/v6.5/source/include/linux/minmax.h

Cheers,

Joel
Cédric Le Goater Sept. 27, 2023, 5:44 a.m. UTC | #6
On 9/27/23 04:12, Joel Stanley wrote:
> On Fri, 22 Sept 2023 at 13:21, Cédric Le Goater <clg@kaod.org> wrote:
> 
>>> +    t->start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
>>> +    return calculate_time(t, MAX(MAX(t->match[0], t->match[1]), 0));
>>
>> This MAX(MAX(x, y), 0) looks strange to me. Would you remember where it comes
>> from ? Thanks,
> 
> That looks very strange. I think you've sorted it, so I wanted to
> bring up the MAX macros themselves. It's unfortunate that they create
> a non-unique local variable. Are we allowed to borrow the kernels
> macros? They have some infrastructure for creating a unique local
> variable name, as well as handling the const and non-const variants
> with the one macro.
> 
> https://elixir.bootlin.com/linux/v6.5/source/include/linux/minmax.h

I believe that's what Markus does in its own way :

   https://lore.kernel.org/qemu-devel/20230921121312.1301864-8-armbru@redhat.com/

Thanks,

C.
Markus Armbruster Sept. 27, 2023, 6:46 a.m. UTC | #7
Cédric Le Goater <clg@kaod.org> writes:

> On 9/27/23 04:12, Joel Stanley wrote:
>> On Fri, 22 Sept 2023 at 13:21, Cédric Le Goater <clg@kaod.org> wrote:
>> 
>>>> +    t->start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
>>>> +    return calculate_time(t, MAX(MAX(t->match[0], t->match[1]), 0));
>>>
>>> This MAX(MAX(x, y), 0) looks strange to me. Would you remember where it comes
>>> from ? Thanks,
>> That looks very strange. I think you've sorted it, so I wanted to
>> bring up the MAX macros themselves. It's unfortunate that they create
>> a non-unique local variable. Are we allowed to borrow the kernels
>> macros? They have some infrastructure for creating a unique local
>> variable name, as well as handling the const and non-const variants
>> with the one macro.
>> https://elixir.bootlin.com/linux/v6.5/source/include/linux/minmax.h
>
> I believe that's what Markus does in its own way :
>
>   https://lore.kernel.org/qemu-devel/20230921121312.1301864-8-armbru@redhat.com/

I wasn't aware of the kernel's infrastructure.  We can steal it if
people think it provides additional value.  Make your case :)
diff mbox series

Patch

diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c
index 5c786e512815..9ffd8e09f670 100644
--- a/hw/timer/aspeed_timer.c
+++ b/hw/timer/aspeed_timer.c
@@ -109,37 +109,40 @@  static inline uint64_t calculate_time(struct AspeedTimer *t, uint32_t ticks)
 
 static uint64_t calculate_next(struct AspeedTimer *t)
 {
-    uint64_t next = 0;
-    uint32_t rate = calculate_rate(t);
+    uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+    uint64_t next;
 
-    while (!next) {
-        /* We don't know the relationship between the values in the match
-         * registers, so sort using MAX/MIN/zero. We sort in that order as the
-         * timer counts down to zero. */
-        uint64_t seq[] = {
-            calculate_time(t, MAX(t->match[0], t->match[1])),
-            calculate_time(t, MIN(t->match[0], t->match[1])),
-            calculate_time(t, 0),
-        };
-        uint64_t reload_ns;
-        uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-
-        if (now < seq[0]) {
-            next = seq[0];
-        } else if (now < seq[1]) {
-            next = seq[1];
-        } else if (now < seq[2]) {
-            next = seq[2];
-        } else if (t->reload) {
-            reload_ns = muldiv64(t->reload, NANOSECONDS_PER_SECOND, rate);
-            t->start = now - ((now - t->start) % reload_ns);
-        } else {
-            /* no reload value, return 0 */
-            break;
-        }
+    /*
+     * We don't know the relationship between the values in the match
+     * registers, so sort using MAX/MIN/zero. We sort in that order as
+     * the timer counts down to zero.
+     */
+
+    next = calculate_time(t, MAX(t->match[0], t->match[1]));
+    if (now < next) {
+        return next;
+    }
+
+    next = calculate_time(t, MIN(t->match[0], t->match[1]));
+    if (now < next) {
+        return next;
+    }
+
+    next = calculate_time(t, 0);
+    if (now < next) {
+        return next;
+    }
+
+    /* We've missed all deadlines, fire interrupt and try again */
+    timer_del(&t->timer);
+
+    if (timer_overflow_interrupt(t)) {
+        t->level = !t->level;
+        qemu_set_irq(t->irq, t->level);
     }
 
-    return next;
+    t->start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+    return calculate_time(t, MAX(MAX(t->match[0], t->match[1]), 0));
 }
 
 static void aspeed_timer_mod(AspeedTimer *t)