Re: [PATCH 0/4] block: drain per-cpu latency stats over possible CPUs

"yu kuai" <[email protected]> Mon, 3 Aug 2026 13:30:58 +0800
Newsgroups org.kernel.vger.cgroups,org.kernel.vger.linux-block,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
=E5=9C=A8 2026/7/20 17:37, Tao Cui =E5=86=99=E9=81=93:

> From: Tao Cui <[email protected]>
>
> Several block-layer latency statistics accumulate samples in per-cpu
> buckets and drain them periodically -- in timer callbacks, or at each
> check / reporting site -- using for_each_online_cpu().  When a CPU that
> holds pending samples is taken offline, that bucket is skipped during
> the drain: the samples are neither accumulated into the current window
> nor, where the drain also resets, cleared.  They sit in the bucket until
> the CPU is brought back online, at which point they are flushed into
> whatever window happens to be running.
>
> The effect cuts both ways and is harmful in each direction:
>
>    - while the CPU is offline, its samples are under-counted, so
>      throttle / latency / scheduling decisions derived from these stats
>      miss the work that actually happened ("should have throttled, but
>      didn't");
>
>    - on re-online, a burst of stale samples lands in a later window and
>      can spuriously trip a throttle or skew a vrate / latency estimate
>      ("shouldn't throttle, but did").
>
> Fix: drain over for_each_possible_cpu() instead.  The per-cpu areas are
> allocated for the full possible set, and the init paths already iterate
> it; an offline CPU has no concurrent writer, so reading and resetting
> its bucket is safe.
>
> Cost is bounded: the extra work is over offline buckets that have no
> writer and -- because each drain sums and resets -- hold a zeroed stat
> after the first post-offline drain.  It amounts to O(possible - online)
> trivial per-cpu reads on a timer / check / show path, alongside the
> per-CPU stat work already done for online CPUs; on systems where all
> possible CPUs are online it is exactly zero.
>
> This is the same one-line mistake in four sites; each patch switches its
> drain loop from for_each_online_cpu() to for_each_possible_cpu():
>
>    - block/blk-stat.c        blk_stat_timer_fn() -- shared infrastructure
>                              that also feeds wbt and blk-mq latency
>    - block/blk-iolatency.c   iolatency_check_latencies() (throttle) and
>                              iolatency_ssd_stat() (io.stat reporting)
>    - block/blk-iocost.c      ioc_lat_stat() -- missed-ppm / rq_wait delta
>    - block/kyber-iosched.c   kyber_timer_fn() -- latency histogram flush
>
> blk-mq.c's for_each_online_cpu() at the hctx-has-online-cpu check is
> correct and left untouched -- it is not a statistics drain.
>
> Tao Cui (4):
>    block/blk-stat: drain per-cpu callback stats over possible CPUs
>    block/blk-iolatency: account per-cpu latency stats over possible CPUs
>    block/blk-iocost: collect per-cpu latency stats over possible CPUs
>    block/kyber-iosched: flush per-cpu latency buckets over possible CPUs
>
>   block/blk-iocost.c     | 2 +-
>   block/blk-iolatency.c  | 4 ++--
>   block/blk-stat.c       | 2 +-
>   block/kyber-iosched.c  | 2 +-
>   4 files changed, 5 insertions(+), 5 deletions(-)
>
> --
> 2.43.0'

For this set:

Reviewed-by: Yu Kuai <[email protected]>

>
--=20
Thanks,
Kuai