[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