qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Luc Michel <luc.michel@greensocs.com>
To: Peter Maydell <peter.maydell@linaro.org>, qemu-devel@nongnu.org
Cc: "Aleksandar Rikalo" <aleksandar.rikalo@syrmia.com>,
	"Richard Henderson" <richard.henderson@linaro.org>,
	"Philippe Mathieu-Daudé" <f4bug@amsat.org>,
	"Jiaxun Yang" <jiaxun.yang@flygoat.com>
Subject: Re: [PATCH v2 1/4] clock: Introduce clock_ticks_to_ns()
Date: Tue, 15 Dec 2020 20:48:18 +0100	[thread overview]
Message-ID: <3b820baf-948d-0ed9-8e0c-a7d132834b3a@greensocs.com> (raw)
In-Reply-To: <20201215150929.30311-2-peter.maydell@linaro.org>

On 12/15/20 4:09 PM, Peter Maydell wrote:
> The clock_get_ns() API claims to return the period of a clock in
> nanoseconds. Unfortunately since it returns an integer and a
> clock's period is represented in units of 2^-32 nanoseconds,
> the result is often an approximation, and calculating a clock
> expiry deadline by multiplying clock_get_ns() by a number-of-ticks
> is unacceptably inaccurate.
> 
> Introduce a new API clock_ticks_to_ns() which returns the number
> of nanoseconds it takes the clock to make a given number of ticks.
> This function can do the complete calculation internally and
> will thus give a more accurate result.
> 
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

Reviewed-by: Luc Michel <luc@lmichel.fr>

> ---
> The 64x64->128 multiply is a bit painful for 32-bit and I
> guess in theory since we know we only want bits [95:32]
> of the result we could special-case it, but TBH I don't
> think 32-bit hosts merit much optimization effort these days.
> 
> Changes in v2: saturate the result to INT64_MAX.
> ---
>   docs/devel/clocks.rst | 29 +++++++++++++++++++++++++++++
>   include/hw/clock.h    | 41 +++++++++++++++++++++++++++++++++++++++++
>   2 files changed, 70 insertions(+)
> 
> diff --git a/docs/devel/clocks.rst b/docs/devel/clocks.rst
> index e5da28e2111..c2e70e64db1 100644
> --- a/docs/devel/clocks.rst
> +++ b/docs/devel/clocks.rst
> @@ -258,6 +258,35 @@ Here is an example:
>                           clock_get_ns(dev->my_clk_input));
>       }
>   
> +Calculating expiry deadlines
> +----------------------------
> +
> +A commonly required operation for a clock is to calculate how long
> +it will take for the clock to tick N times; this can then be used
> +to set a timer expiry deadline. Use the function ``clock_ticks_to_ns()``,
> +which takes an unsigned 64-bit count of ticks and returns the length
> +of time in nanoseconds required for the clock to tick that many times.
> +
> +It is important not to try to calculate expiry deadlines using a
> +shortcut like multiplying a "period of clock in nanoseconds" value
> +by the tick count, because clocks can have periods which are not a
> +whole number of nanoseconds, and the accumulated error in the
> +multiplication can be significant.
> +
> +For a clock with a very long period and a large number of ticks,
> +the result of this function could in theory be too large to fit in
> +a 64-bit value. To avoid overflow in this case, ``clock_ticks_to_ns()``
> +saturates the result to INT64_MAX (because this is the largest valid
> +input to the QEMUTimer APIs). Since INT64_MAX nanoseconds is almost
> +300 years, anything with an expiry later than that is in the "will
> +never happen" category. Callers of ``clock_ticks_to_ns()`` should
> +therefore generally not special-case the possibility of a saturated
> +result but just allow the timer to be set to that far-future value.
> +(If you are performing further calculations on the returned value
> +rather than simply passing it to a QEMUTimer function like
> +``timer_mod_ns()`` then you should be careful to avoid overflow
> +in those calculations, of course.)
> +
>   Changing a clock period
>   -----------------------
>   
> diff --git a/include/hw/clock.h b/include/hw/clock.h
> index 81bcf3e505a..b5fff6ded83 100644
> --- a/include/hw/clock.h
> +++ b/include/hw/clock.h
> @@ -16,6 +16,8 @@
>   
>   #include "qom/object.h"
>   #include "qemu/queue.h"
> +#include "qemu/host-utils.h"
> +#include "qemu/bitops.h"
>   
>   #define TYPE_CLOCK "clock"
>   OBJECT_DECLARE_SIMPLE_TYPE(Clock, CLOCK)
> @@ -218,6 +220,45 @@ static inline unsigned clock_get_ns(Clock *clk)
>       return CLOCK_PERIOD_TO_NS(clock_get(clk));
>   }
>   
> +/**
> + * clock_ticks_to_ns:
> + * @clk: the clock to query
> + * @ticks: number of ticks
> + *
> + * Returns the length of time in nanoseconds for this clock
> + * to tick @ticks times. Because a clock can have a period
> + * which is not a whole number of nanoseconds, it is important
> + * to use this function when calculating things like timer
> + * expiry deadlines, rather than attempting to obtain a "period
> + * in nanoseconds" value and then multiplying that by a number
> + * of ticks.
> + *
> + * The result could in theory be too large to fit in a 64-bit
> + * value if the number of ticks and the clock period are both
> + * large; to avoid overflow the result will be saturated to INT64_MAX
> + * (because this is the largest valid input to the QEMUTimer APIs).
> + * Since INT64_MAX nanoseconds is almost 300 years, anything with
> + * an expiry later than that is in the "will never happen" category
> + * and callers can reasonably not special-case the saturated result.
> + */
> +static inline uint64_t clock_ticks_to_ns(const Clock *clk, uint64_t ticks)
> +{
> +    uint64_t ns_low, ns_high;
> +
> +    /*
> +     * clk->period is the period in units of 2^-32 ns, so
> +     * (clk->period * ticks) is the required length of time in those
> +     * units, and we can convert to nanoseconds by multiplying by
> +     * 2^32, which is the same as shifting the 128-bit multiplication
> +     * result right by 32.
> +     */
> +    mulu64(&ns_low, &ns_high, clk->period, ticks);
> +    if (ns_high & MAKE_64BIT_MASK(31, 33)) {
> +        return INT64_MAX;
> +    }
> +    return ns_low >> 32 | ns_high << 32;
> +}
> +
>   /**
>    * clock_is_enabled:
>    * @clk: a clock
> 


  parent reply	other threads:[~2020-12-15 19:51 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-12-15 15:09 [PATCH v2 0/4] clock: Get rid of clock_get_ns() Peter Maydell
2020-12-15 15:09 ` [PATCH v2 1/4] clock: Introduce clock_ticks_to_ns() Peter Maydell
2020-12-15 15:21   ` Richard Henderson
2020-12-15 19:48   ` Luc Michel [this message]
2020-12-15 15:09 ` [PATCH v2 2/4] target/mips: Don't use clock_get_ns() in clock period calculation Peter Maydell
2020-12-15 15:09 ` [PATCH v2 3/4] clock: Remove clock_get_ns() Peter Maydell
2020-12-15 15:09 ` [PATCH v2 4/4] clock: Define and use new clock_display_freq() Peter Maydell
2020-12-15 15:29 ` [PATCH v2 0/4] clock: Get rid of clock_get_ns() Philippe Mathieu-Daudé
2020-12-15 23:08   ` Philippe Mathieu-Daudé
2021-01-01 20:35 ` Philippe Mathieu-Daudé
2021-01-03 13:47   ` Philippe Mathieu-Daudé

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=3b820baf-948d-0ed9-8e0c-a7d132834b3a@greensocs.com \
    --to=luc.michel@greensocs.com \
    --cc=aleksandar.rikalo@syrmia.com \
    --cc=f4bug@amsat.org \
    --cc=jiaxun.yang@flygoat.com \
    --cc=peter.maydell@linaro.org \
    --cc=qemu-devel@nongnu.org \
    --cc=richard.henderson@linaro.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).