[RFC qemu legoater/aspeed-3.1 4/4] timer: aspeed: Provide back-pressure information for short periods

Cédric Le Goater clg at kaod.org
Mon Jan 14 19:55:13 AEDT 2019


On 1/14/19 12:07 AM, Andrew Jeffery wrote:
> On Fri, 11 Jan 2019, at 20:40, Cédric Le Goater wrote:
>> On 1/11/19 4:56 AM, Andrew Jeffery wrote:
>>> First up: This is not the way the hardware behaves.
>>
>> I think this is OK from the QEMU side. It would be better if Linux was 
>> not involved though.
> 
> It doesn't have to be, just it may see wonky timer behaviour because
> the timer will not be doing exactly what was asked.
> 
>>
>>> However, it helps resolve real-world problems with short periods being
>>> used under Linux. Commit 4451d3f59f2a ("clocksource/drivers/fttmr010:
>>> Fix set_next_event handler") in Linux fixed the timer driver to
>>> correctly schedule the next event for the Aspeed controller, and in
>>> combination with 5daa8212c08e ("ARM: dts: aspeed: Describe random number
>>> device") Linux will now set a timer with a period as low as 1us.
>>>
>>> Configuring a qemu timer with such a short period results in spending
>>> time handling the interrupt in the model rather than executing guest
>>> code, leading to noticeable "sticky" behaviour in the guest.
>>>
>>> The behaviour of Linux is correct with respect to the hardware, so we
>>> need to improve our handling under emulation. The approach chosen is to
>>> provide back-pressure information by calculating an acceptable minimum
>>> number of ticks to be set on the model. Under Linux an additional read
>>> is added in the timer configuration path to detect back-pressure, which
>>> will never occur on hardware. However if back-pressure is observed, the
>>> driver alerts the clock event subsystem, which then performs its own
>>> next event dilation via a config option - d1748302f70b ("clockevents:
>>> Make minimum delay adjustments configurable")
>>
>> So, this is approach is not totally unacceptable, QEMU is an hypervisor
>> which has its own timing constraints.
>>  
>>> A minimum period of 5us was experimentally determined on a Lenovo
>>> T480s, which I've increased to 20us for "safety".
>>>
>>> Signed-off-by: Andrew Jeffery <andrew at aj.id.au>
>>> ---
>>>  hw/misc/aspeed_scu.c            | 6 ++++++
>>>  hw/timer/aspeed_timer.c         | 6 +++++-
>>>  include/hw/timer/aspeed_timer.h | 1 +
>>>  3 files changed, 12 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c
>>> index 257f9a6c6b8a..0410776b456a 100644
>>> --- a/hw/misc/aspeed_scu.c
>>> +++ b/hw/misc/aspeed_scu.c
>>> @@ -432,6 +432,12 @@ static void aspeed_scu_realize(DeviceState *dev, Error **errp)
>>>                            TYPE_ASPEED_SCU, SCU_IO_REGION_SIZE);
>>>  
>>>      sysbus_init_mmio(sbd, &s->iomem);
>>> +
>>> +    /*
>>> +     * Reset on realize to ensure the APB clock value is calculated in time for
>>> +     * use by the timer model, which is reset before the SCU.
>>> +     */
>>> +    aspeed_scu_reset(dev);
>>
>> sigh. May be we should call aspeed_scu_set_apb_freq() from the Aspeed timer
>> model ?
> 
> I dunno - will we run into this in other models as well? If we do, should they also call
> aspeed_scu_set_apb_freq()? If they do it probably won't matter (same input, same
> output), but it seems a bit messy to distribute the call through the code?

This is really a reset order problem. SCU reset should be called before all 
others models. I don't know if we can order the calls but calling reset in 
a realize routine is a problem also. 

The APB clock depends on the HPLL_PARAM register and on the CLK_SEL register,
and these can be set a runtime by FW.

May be we can introduce a reset method at the machine level forcing SCU
first. That  would be one way to ask how it should be done.

C.



>>
>>>  }
>>>  
>>>  static const VMStateDescription vmstate_aspeed_scu = {
>>> diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c
>>> index 35b40a7c4010..0f3501ac5a5c 100644
>>> --- a/hw/timer/aspeed_timer.c
>>> +++ b/hw/timer/aspeed_timer.c
>>> @@ -254,7 +254,7 @@ static void aspeed_timer_set_value(AspeedTimerCtrlState *s, int timer, int reg,
>>>      switch (reg) {
>>>      case TIMER_REG_RELOAD:
>>>          old_reload = t->reload;
>>> -        t->reload = value;
>>> +        t->reload = value < t->min_ticks ? t->min_ticks : value;
>>>  
>>>          /* If the reload value was not previously set, or zero, and
>>>           * the current value is valid, try to start the timer if it is
>>> @@ -306,7 +306,11 @@ static void aspeed_timer_ctrl_enable(AspeedTimer *t, bool enable)
>>>  
>>>  static void aspeed_timer_ctrl_external_clock(AspeedTimer *t, bool enable)
>>>  {
>>> +    AspeedTimerCtrlState *s = timer_to_ctrl(t);
>>> +    uint32_t rate = enable ? TIMER_CLOCK_EXT_HZ : s->scu->apb_freq;
>>> +
>>>      trace_aspeed_timer_ctrl_external_clock(t->id, enable);
>>> +    t->min_ticks = muldiv64(20 * SCALE_US, rate, NANOSECONDS_PER_SECOND);
>>
>> That '20' deserves a define I think.
> 
> Yes, good call :)
> 
> Andrew
> 



More information about the openbmc mailing list