On 7/13/26 02:36, Andrey Zhadchenko wrote:
> The dm-stats latency histogram accounts the service time of an I/O:
> the time between the moment device-mapper starts the request and its
> completion.  Under high load, requests may spend a noticeable part
> of their life queued before dispatch. The service time then differs
> from the latency observed by userspace.
> 
> Add a new region feature argument, hist_total_latency, which makes
> the histogram account the total time the bio spent in the block
> layer instead. The elapsed time is derived from the bio issue
> timestamp maintained by the block cgroup infrastructure
> (bio->bi_issue), exposed by the previously added
> bio_issue_elapsed_ns().
> 
> Only the histogram is affected: all other counters keep reporting
> the service time. The option requires precise_timestamps (the
> total latency is measured in nanoseconds) and a histogram to be
> specified.
> 
> https://virtuozzo.atlassian.net/browse/VSTOR-103846
> Signed-off-by: Andrey Zhadchenko <[email protected]>
> ---
>  .../admin-guide/device-mapper/statistics.rst  | 19 ++++++++++++++---
>  drivers/md/dm-rq.c                            |  4 ++++
>  drivers/md/dm-stats.c                         | 21 ++++++++++++++++---
>  drivers/md/dm-stats.h                         |  3 +++
>  drivers/md/dm.c                               |  4 ++++
>  5 files changed, 45 insertions(+), 6 deletions(-)
> 
> diff --git a/Documentation/admin-guide/device-mapper/statistics.rst 
> b/Documentation/admin-guide/device-mapper/statistics.rst
> index 41ded0bc59335..d5b1b1013c087 100644
> --- a/Documentation/admin-guide/device-mapper/statistics.rst
> +++ b/Documentation/admin-guide/device-mapper/statistics.rst
> @@ -70,6 +70,18 @@ Messages
>               used, the resulting times are in nanoseconds instead of
>               milliseconds.  Precise timestamps are a little bit slower
>               to obtain than jiffies-based timestamps.
> +       hist_total_latency
> +             make the latency histogram account the total time a
> +             request spent in the block layer, from bio issue to
> +             completion, instead of just the service time observed by
> +             device-mapper.  Under high load, when requests spend a
> +             significant time queued, the total latency is what
> +             userspace actually observes.  This option requires
> +             precise_timestamps and a histogram, and only affects the
> +             histogram counters; it needs the bio issue timestamp,
> +             which is only maintained when CONFIG_BLK_CGROUP is
> +             enabled (without it, the histogram keeps accounting the
> +             service time).
>         histogram:n1,n2,n3,n4,...
>               collect histogram of latencies.  The
>               numbers n1, n2, etc are times that represent the boundaries
> @@ -124,10 +136,11 @@ Messages
>  
>       Output format:
>         <region_id>: <start_sector>+<length> <step> <program_id> <aux_data>
> -             precise_timestamps histogram:n1,n2,n3,...
> +             precise_timestamps hist_total_latency histogram:n1,n2,n3,...
>  
> -     The strings "precise_timestamps" and "histogram" are printed only
> -     if they were specified when creating the region.
> +     The strings "precise_timestamps", "hist_total_latency" and
> +     "histogram" are printed only if they were specified when creating
> +     the region.
>  
>      @stats_print <region_id> [<starting_line> <number_of_lines>]
>       Print counters for each step-sized area of a region.
> diff --git a/drivers/md/dm-rq.c b/drivers/md/dm-rq.c
> index 58ad4c94d6119..28bd496143770 100644
> --- a/drivers/md/dm-rq.c
> +++ b/drivers/md/dm-rq.c
> @@ -129,6 +129,10 @@ static void rq_end_stats(struct mapped_device *md, 
> struct request *orig)
>       if (unlikely(dm_stats_used(&md->stats))) {
>               struct dm_rq_target_io *tio = tio_from_request(orig);
>  
> +             if (md->stats.hist_total_latency && orig->bio)
> +                     tio->stats_aux.histogram_duration_ns =
> +                             bio_issue_elapsed_ns(orig->bio);
> +
>               dm_stats_account_io(&md->stats, rq_data_dir(orig),
>                                   blk_rq_pos(orig), tio->n_sectors, true,
>                                   tio->duration_jiffies, &tio->stats_aux);
> diff --git a/drivers/md/dm-stats.c b/drivers/md/dm-stats.c
> index 1e5d988f44da6..2ede4b51c5a4c 100644
> --- a/drivers/md/dm-stats.c
> +++ b/drivers/md/dm-stats.c
> @@ -60,6 +60,7 @@ struct dm_stat {
>  };
>  
>  #define STAT_PRECISE_TIMESTAMPS              1
> +#define STAT_HIST_TOTAL_LATENCY              2
>  
>  struct dm_stats_last_position {
>       sector_t last_sector;
> @@ -246,15 +247,17 @@ static void dm_stats_recalc_precise_timestamps(struct 
> dm_stats *stats)
>       struct list_head *l;
>       struct dm_stat *tmp_s;
>       bool precise_timestamps = false;
> +     bool hist_total_latency = false;
>  
>       list_for_each(l, &stats->list) {
>               tmp_s = container_of(l, struct dm_stat, list_entry);
> -             if (tmp_s->stat_flags & STAT_PRECISE_TIMESTAMPS) {
> +             if (tmp_s->stat_flags & STAT_PRECISE_TIMESTAMPS)
>                       precise_timestamps = true;
> -                     break;
> -             }
> +             if (tmp_s->stat_flags & STAT_HIST_TOTAL_LATENCY)
> +                     hist_total_latency = true;
>       }

1) Maybe we should not reuse dm_stats_recalc_precise_timestamps here and 
introduce either
dm_stats_recalc_hist_total_latency() or even common dm_stats_recalc().

Calculating hlist_total_latency in dm_stats_recalc_precise_timestamps() is 
obfuscating
the code.

2) Missing initialization of hlist_total_latency in dm_stats_init() along with
precise_timestamps's one makes me think that there could be some race where
our new variable is used uninitialized. Even if no, I'd add initialization there
for just uniformity.

>       stats->precise_timestamps = precise_timestamps;
> +     stats->hist_total_latency = hist_total_latency;
>  }
>  
>  static int dm_stats_create(struct dm_stats *stats, sector_t start, sector_t 
> end,
> @@ -306,6 +309,10 @@ static int dm_stats_create(struct dm_stats *stats, 
> sector_t start, sector_t end,
>       if ((n_histogram_entries + 1) * (size_t)n_entries > 
> DM_STAT_MAX_HISTOGRAM_ENTRIES)
>               return -EOVERFLOW;
>  
> +     if ((stat_flags & STAT_HIST_TOTAL_LATENCY) &&
> +         (!n_histogram_entries || !(stat_flags & STAT_PRECISE_TIMESTAMPS)))
> +             return -EINVAL;
> +
>       if (!check_shared_memory(shared_alloc_size + histogram_alloc_size +
>                                num_possible_cpus() * (percpu_alloc_size + 
> histogram_alloc_size)))
>               return -ENOMEM;
> @@ -509,6 +516,8 @@ static int dm_stats_list(struct dm_stats *stats, const 
> char *program,
>                               s->aux_data);
>                       if (s->stat_flags & STAT_PRECISE_TIMESTAMPS)
>                               DMEMIT(" precise_timestamps");
> +                     if (s->stat_flags & STAT_HIST_TOTAL_LATENCY)
> +                             DMEMIT(" hist_total_latency");
>                       if (s->n_histogram_entries) {
>                               unsigned int i;
>  
> @@ -612,6 +621,10 @@ static void dm_stat_for_entry(struct dm_stat *s, size_t 
> entry,
>               if (s->n_histogram_entries) {
>                       unsigned int lo = 0, hi = s->n_histogram_entries + 1;
>  
> +                     if ((s->stat_flags & STAT_HIST_TOTAL_LATENCY) &&
> +                         stats_aux->histogram_duration_ns)
> +                             duration = stats_aux->histogram_duration_ns;

This hunk logically belongs to the code above:

                if (!(s->stat_flags & STAT_PRECISE_TIMESTAMPS)) {
                        p->ticks[idx] += duration_jiffies;
                        duration = jiffies_to_msecs(duration_jiffies);
                } else {
                        p->ticks[idx] += stats_aux->duration_ns;
                        if (s->stat_flags & STAT_HIST_TOTAL_LATENCY)
                                duration = stats_aux->histogram_duration_ns;
                        else
                                duration = stats_aux->duration_ns;
                }

No? Mixing duration setting code with other code, probably, only obfuscates 
things.


> +
>                       while (lo + 1 < hi) {
>                               unsigned int mid = (lo + hi) / 2;
>  
> @@ -1063,6 +1076,8 @@ static int message_stats_create(struct mapped_device 
> *md,
>                               goto ret_einval;
>                       if (!strcasecmp(a, "precise_timestamps"))
>                               stat_flags |= STAT_PRECISE_TIMESTAMPS;
> +                     else if (!strcasecmp(a, "hist_total_latency"))
> +                             stat_flags |= STAT_HIST_TOTAL_LATENCY;
>                       else if (!strncasecmp(a, "histogram:", 10)) {
>                               if (n_histogram_entries)
>                                       goto ret_einval;
> diff --git a/drivers/md/dm-stats.h b/drivers/md/dm-stats.h
> index c6728c8b41594..cce68414b97ee 100644
> --- a/drivers/md/dm-stats.h
> +++ b/drivers/md/dm-stats.h
> @@ -14,11 +14,13 @@ struct dm_stats {
>       struct list_head list;  /* list of struct dm_stat */
>       struct dm_stats_last_position __percpu *last;
>       bool precise_timestamps;
> +     bool hist_total_latency;
>  };
>  
>  struct dm_stats_aux {
>       bool merged;
>       unsigned long long duration_ns;
> +     unsigned long long histogram_duration_ns;
>  };
>  
>  int dm_stats_init(struct dm_stats *st);
> @@ -41,6 +43,7 @@ static inline bool dm_stats_used(struct dm_stats *st)
>  
>  static inline void dm_stats_record_start(struct dm_stats *stats, struct 
> dm_stats_aux *aux)
>  {
> +     aux->histogram_duration_ns = 0;
>       if (unlikely(stats->precise_timestamps))
>               aux->duration_ns = ktime_to_ns(ktime_get());
>  }
> diff --git a/drivers/md/dm.c b/drivers/md/dm.c
> index 130eec93fa10e..7029201480358 100644
> --- a/drivers/md/dm.c
> +++ b/drivers/md/dm.c
> @@ -569,6 +569,10 @@ static void dm_io_acct(struct dm_io *io, bool end)
>           unlikely(dm_stats_used(&io->md->stats))) {
>               sector_t sector;
>  
> +             if (end && io->md->stats.hist_total_latency)
> +                     io->stats_aux.histogram_duration_ns =
> +                             bio_issue_elapsed_ns(bio);
> +
>               if (unlikely(dm_io_flagged(io, DM_IO_WAS_SPLIT)))
>                       sector = bio_end_sector(bio) - io->sector_offset;
>               else

-- 
Best regards, Pavel Tikhomirov
Senior Software Developer, Virtuozzo.

_______________________________________________
Devel mailing list
[email protected]
https://lists.openvz.org/mailman/listinfo/devel

Reply via email to