From: Markus Armbruster <armbru@redhat.com>
To: Alberto Garcia <berto@igalia.com>
Cc: Wang Liang <wangliangzz@126.com>,
qemu-devel@nongnu.org, qemu-block@nongnu.org,
pbonzini@redhat.com, stefanha@redhat.com,
silbe@linux.vnet.ibm.com, Wang Liang <wangliangzz@inspur.com>
Subject: Re: [PATCH] ratelimit: restrict the delay time to a non-negative value
Date: Fri, 23 Sep 2022 09:14:16 +0200 [thread overview]
Message-ID: <878rmah8af.fsf@pond.sub.org> (raw)
In-Reply-To: <w511qs5w38j.fsf@igalia.com> (Alberto Garcia's message of "Wed, 21 Sep 2022 08:17:32 +0000")
Alberto Garcia <berto@igalia.com> writes:
> On Wed 21 Sep 2022 09:47:32 AM +08, Wang Liang wrote:
>>> > - return limit->slice_end_time - now;
>>> > + return MAX(limit->slice_end_time - now, 0);
>>>
>>> How can this be negative? slice_end_time is guaranteed to be larger
>>> than
>>> now:
>>>
>>> if (limit->slice_end_time < now) {
>>> /* Previous, possibly extended, time slice finished; reset
>>> the
>>> * accounting. */
>>> limit->slice_start_time = now;
>>> limit->slice_end_time = now + limit->slice_ns;
>>> limit->dispatched = 0;
>>> }
>>>
>> This is just a guarantee.
>>
>> If slice_end_time is assigned later by
>> limit->slice_end_time = limit->slice_start_time +
>> (uint64_t)(delay_slices * limit->slice_ns);
>> There may be precision issues at that time.
>
> Ok, on a closer look, if at the start of the function
>
> limit->slice_start_time < now, and
> limit->slice_end_time >= now
>
> it seems that in principle what you say can happen.
How? Let's see.
static inline int64_t ratelimit_calculate_delay(RateLimit *limit, uint64_t n)
{
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
What kind of clock is QEMU_CLOCK_REALTIME? See below.
double delay_slices;
QEMU_LOCK_GUARD(&limit->lock);
if (!limit->slice_quota) {
/* Throttling disabled. */
return 0;
}
assert(limit->slice_ns);
if (limit->slice_end_time < now) {
This is false.
/* Previous, possibly extended, time slice finished; reset the
* accounting. */
limit->slice_start_time = now;
limit->slice_end_time = now + limit->slice_ns;
limit->dispatched = 0;
}
limit->dispatched += n;
This is in theory vulnerable to wrap-around.
if (limit->dispatched < limit->slice_quota) {
This must be false (or else we return 0, which isn't negative).
/* We may send further data within the current time slice, no
* need to delay the next request. */
return 0;
}
/* Quota exceeded. Wait based on the excess amount and then start a new
* slice. */
delay_slices = (double)limit->dispatched / limit->slice_quota;
Both @dispatched and @slice_quota are uint64_t. Conversion to double
may lose precision, but cant't change the sign. Therefore,
@delay_slices is non-negative.
limit->slice_end_time = limit->slice_start_time +
(uint64_t)(delay_slices * limit->slice_ns);
Conversion from double to uint64_t has undefined behavior when the value
is not representable after truncation towards zero. So, if the
multiplication's result truncated towards zero exceeds UINT_MAX, we're
theoretically toast.
To return a negative value, @slice_end_time must become less than @now
here.
return limit->slice_end_time - now;
}
This is how far I get without (laboriously!) reconstructing what the
members of struct RateLimit actually mean, and what its invariants are,
if any. We could write down such things in comments, but we prefer to
keep things fresh and spicy, and developers confused.
Can you elaborate on the "precision issues"?
> If it's so, it would be good to know under what conditions this happens,
> because this hasn't changed in years.
>
> Berto
prev parent reply other threads:[~2022-09-23 7:19 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-09-20 12:33 [PATCH] ratelimit: restrict the delay time to a non-negative value wangliangzz
2022-09-20 13:18 ` Alberto Garcia
2022-09-21 1:47 ` Wang Liang
2022-09-21 4:53 ` Markus Armbruster
2022-09-21 6:26 ` Wang Liang
2022-09-21 8:17 ` Alberto Garcia
2022-09-23 7:14 ` Markus Armbruster [this message]
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=878rmah8af.fsf@pond.sub.org \
--to=armbru@redhat.com \
--cc=berto@igalia.com \
--cc=pbonzini@redhat.com \
--cc=qemu-block@nongnu.org \
--cc=qemu-devel@nongnu.org \
--cc=silbe@linux.vnet.ibm.com \
--cc=stefanha@redhat.com \
--cc=wangliangzz@126.com \
--cc=wangliangzz@inspur.com \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.