[Devel] [PATCH VZ10 02/11] block/bio: add bio_issue_elapsed_ns()
Pavel Tikhomirov
ptikhomirov at virtuozzo.com
Fri Jul 24 13:51:08 MSK 2026
On 7/23/26 22:17, Andrey Zhadchenko wrote:
>
>
> On 7/17/26 17:06, Pavel Tikhomirov wrote:
>>
>>
>> On 7/13/26 02:36, Andrey Zhadchenko wrote:
>>> If blkcgroup is present, each bio is stamped at the issue time.
>>> Expose this via bio_issue_elapsed_ns() so we can accurately
>>> calculate total time spent in the block layer.
>>>
>>> https://virtuozzo.atlassian.net/browse/VSTOR-103846
>>> Signed-off-by: Andrey Zhadchenko <andrey.zhadchenko at virtuozzo.com>
>>> ---
>>> block/bio.c | 23 +++++++++++++++++++++++
>>> include/linux/bio.h | 2 ++
>>> 2 files changed, 25 insertions(+)
>>>
>>> diff --git a/block/bio.c b/block/bio.c
>>> index 3c0a558c90f52..c26babfc5e1ce 100644
>>> --- a/block/bio.c
>>> +++ b/block/bio.c
>>> @@ -1827,6 +1827,29 @@ int bioset_init(struct bio_set *bs,
>>> }
>>> EXPORT_SYMBOL(bioset_init);
>>> +/*
>>> + * bio_issue_elapsed_ns - calculate elapsed time since issue
>>> + * @bio: bio
>>> + *
>>> + * Return the elapsed nanoseconds since bio issue until now,
>>> + * handling the 51-bit wraparound.
>>> + */
>>> +u64 bio_issue_elapsed_ns(struct bio *bio)
>>> +{
>>> +#ifdef CONFIG_BLK_CGROUP
>>> + u64 now, elapsed;
>>> +
>>> + /* Mask both operands to 51 bits before the modular subtraction. */
>>> + now = __bio_issue_time(blk_time_get_ns());
>>> + elapsed = now - bio_issue_time(&bio->bi_issue);
>>> +
>>> + return elapsed & BIO_ISSUE_TIME_MASK;
>>
>> In ideal world (if now >= elapsed) this masking is not needed.
>> Otherwise, I guess, it does not help either.
>> See how iolatency_record_time() does no post-masking.
>
> It is helping though. BIO_ISSUE_TIME_MASK is 2^51 - 1. It looks much, but this is nanoseconds and wraps around eventually. Not so fast, but the value seems reachable: 2^51 ns ~ 2*10^15ns ~ 2*10^6s. Given there is 86400 seconds in a day, 2000000/86400 is a somehow comparable to a month. So bi_issue becomes bigger than masked now when they sit around 2^51.
>
> Let's imagine bi->issue is 2^51 - 12345, blk_time_get_ns() returns 2^51 + 12345. So the real elapsed time is 24690.
> Following the code,
> 1. now is truncated to 12345
> 2. elapsed = 12345 - (2^51 - 12345) ->
> unsigned integer underflow is defined behavior, so
> -> 2^64 - 2^51 + 24690
> 3. Then we again apply our mask, 2^64 and 2^51 vanish, so we get 24690.
> Checks!
>
> BUT if the bio spent more then 2^51 ns before completing, we will mask the duration along with it. Probably there will be more serious issues...
>
> We can also skip transition case as you suggest, but then after returning zero some bios may be recorded into wrong buckets as they will default to non-precise service time counting.
> Probably not so critical to bin some bios in the wrong bucket every month. But the wrapping still helps. What do you think?
>
> Also note that mainstream removed the mask and bi_issue is fully 2^64 in latest versions, so we will eventually simplify this helper too and avoid the problem.
Ok, correct, let's leave it as is.
note: In iolatency_record_time() mainstream seem to ignore (now <= start) case.
>
>>
>> In case I miss something and it's needed, consider using __bio_issue_time() helper.
>>
>>> +#else
>>> + return 0;
>>> +#endif
>>> +}
>>> +EXPORT_SYMBOL_GPL(bio_issue_elapsed_ns);
>>> +
>>> static int __init init_bio(void)
>>> {
>>> int i;
>>> diff --git a/include/linux/bio.h b/include/linux/bio.h
>>> index 46ffac5caab78..00c116207a25a 100644
>>> --- a/include/linux/bio.h
>>> +++ b/include/linux/bio.h
>>> @@ -714,4 +714,6 @@ struct bio *bio_chain_and_submit(struct bio *prev, struct bio *new);
>>> struct bio *blk_alloc_discard_bio(struct block_device *bdev,
>>> sector_t *sector, sector_t *nr_sects, gfp_t gfp_mask);
>>> +u64 bio_issue_elapsed_ns(struct bio *bio);
>>> +
>>> #endif /* __LINUX_BIO_H */
>>
>
--
Best regards, Pavel Tikhomirov
Senior Software Developer, Virtuozzo.
More information about the Devel
mailing list