On 7/13/26 02:36, Andrey Zhadchenko wrote:
> A region currently tracks a single latency histogram, whose boundaries
> and per-bucket counters are stored directly in struct dm_stat. In order
> to later add histograms that only account I/O of a particular direction,
> generalize the storage so that a region can hold several independent
> histograms.
> 
> Each histogram is now described by struct dm_stat_histogram, carrying its
> own boundaries, entry count and an offset into the per-entry bucket array.
> The per-entry bucket array is the concatenation of every histogram's
> buckets, and histogram_buckets holds the total. All allocation, clearing,
> summing, printing and listing loops are updated to iterate over
> the histograms and their individual sizes.
> 
> This is a preparatory change with no functional difference.
> 
> https://virtuozzo.atlassian.net/browse/VSTOR-103846
> Signed-off-by: Andrey Zhadchenko <[email protected]>
> ---
>  drivers/md/dm-stats.c | 160 +++++++++++++++++++++++++++++-------------
>  1 file changed, 110 insertions(+), 50 deletions(-)
> 
> diff --git a/drivers/md/dm-stats.c b/drivers/md/dm-stats.c
> index 7a465b5e36cd3..21109ccfa10a9 100644
> --- a/drivers/md/dm-stats.c
> +++ b/drivers/md/dm-stats.c
> @@ -39,6 +39,14 @@ struct dm_stat_shared {
>       struct dm_stat_percpu tmp;
>  };
>  
> +#define DM_STAT_MAX_HISTOGRAMS               1
> +
> +struct dm_stat_histogram {
> +     unsigned int n_entries;
> +     unsigned int offset;
> +     unsigned long long *boundaries;
> +};
> +
>  struct dm_stat {
>       struct list_head list_entry;
>       int id;
> @@ -47,8 +55,9 @@ struct dm_stat {
>       sector_t start;
>       sector_t end;
>       sector_t step;
> -     unsigned int n_histogram_entries;
> -     unsigned long long *histogram_boundaries;
> +     unsigned int n_histograms;
> +     unsigned int histogram_buckets;
> +     struct dm_stat_histogram histograms[DM_STAT_MAX_HISTOGRAMS];
>       const char *program_id;
>       const char *aux_data;
>       struct rcu_head rcu_head;
> @@ -173,9 +182,11 @@ static void dm_kvfree(void *ptr, size_t alloc_size)
>  static void dm_stat_free(struct rcu_head *head)
>  {
>       int cpu;
> +     unsigned int i;
>       struct dm_stat *s = container_of(head, struct dm_stat, rcu_head);
>  
> -     kfree(s->histogram_boundaries);
> +     for (i = 0; i < s->n_histograms; i++)
> +             kfree(s->histograms[i].boundaries);
>       kfree(s->program_id);
>       kfree(s->aux_data);
>       for_each_possible_cpu(cpu) {
> @@ -262,8 +273,8 @@ static void dm_stats_recalc_precise_timestamps(struct 
> dm_stats *stats)
>  
>  static int dm_stats_create(struct dm_stats *stats, sector_t start, sector_t 
> end,
>                          sector_t step, unsigned int stat_flags,
> -                        unsigned int n_histogram_entries,
> -                        unsigned long long *histogram_boundaries,
> +                        unsigned int n_histograms,
> +                        struct dm_stat_histogram *histograms,
>                          const char *program_id, const char *aux_data,
>                          void (*suspend_callback)(struct mapped_device *),
>                          void (*resume_callback)(struct mapped_device *),
> @@ -276,6 +287,8 @@ static int dm_stats_create(struct dm_stats *stats, 
> sector_t start, sector_t end,
>       size_t shared_alloc_size;
>       size_t percpu_alloc_size;
>       size_t histogram_alloc_size;
> +     unsigned int histogram_buckets = 0;
> +     unsigned int hn;
>       struct dm_stat_percpu *p;
>       int cpu;
>       int ret_id;
> @@ -284,6 +297,9 @@ static int dm_stats_create(struct dm_stats *stats, 
> sector_t start, sector_t end,
>       if (end < start || !step)
>               return -EINVAL;
>  
> +     for (hn = 0; hn < n_histograms; hn++)
> +             histogram_buckets += histograms[hn].n_entries + 1;
> +
>       n_entries = end - start;
>       if (dm_sector_div64(n_entries, step))
>               n_entries++;
> @@ -302,15 +318,17 @@ static int dm_stats_create(struct dm_stats *stats, 
> sector_t start, sector_t end,
>       if (percpu_alloc_size / sizeof(struct dm_stat_percpu) != n_entries)
>               return -EOVERFLOW;
>  
> -     histogram_alloc_size = (n_histogram_entries + 1) * (size_t)n_entries * 
> sizeof(unsigned long long);
> -     if (histogram_alloc_size / (n_histogram_entries + 1) != 
> (size_t)n_entries * sizeof(unsigned long long))
> +     histogram_alloc_size = histogram_buckets * (size_t)n_entries * 
> sizeof(unsigned long long);
> +     if (histogram_buckets &&
> +         histogram_alloc_size / histogram_buckets
> +          != (size_t)n_entries * sizeof(unsigned long long))
>               return -EOVERFLOW;
>  
> -     if ((n_histogram_entries + 1) * (size_t)n_entries > 
> DM_STAT_MAX_HISTOGRAM_ENTRIES)
> +     if (histogram_buckets * (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)))
> +         (!histogram_buckets || !(stat_flags & STAT_PRECISE_TIMESTAMPS)))
>               return -EINVAL;
>  
>       if (!check_shared_memory(shared_alloc_size + histogram_alloc_size +
> @@ -330,12 +348,20 @@ static int dm_stats_create(struct dm_stats *stats, 
> sector_t start, sector_t end,
>       s->percpu_alloc_size = percpu_alloc_size;
>       s->histogram_alloc_size = histogram_alloc_size;
>  
> -     s->n_histogram_entries = n_histogram_entries;
> -     s->histogram_boundaries = kmemdup(histogram_boundaries,
> -                                       s->n_histogram_entries * 
> sizeof(unsigned long long), GFP_KERNEL);
> -     if (!s->histogram_boundaries) {
> -             r = -ENOMEM;
> -             goto out;
> +     s->n_histograms = n_histograms;
> +     s->histogram_buckets = histogram_buckets;
> +     for (hn = 0; hn < n_histograms; hn++) {
> +             s->histograms[hn].n_entries = histograms[hn].n_entries;
> +             s->histograms[hn].offset = (hn ? s->histograms[hn - 1].offset +
> +                                         s->histograms[hn - 1].n_entries + 1 
> : 0);
> +             s->histograms[hn].boundaries = 
> kmemdup(histograms[hn].boundaries,
> +                                                    histograms[hn].n_entries 
> *
> +                                                     sizeof(unsigned long 
> long),
> +                                                    GFP_KERNEL);
> +             if (!s->histograms[hn].boundaries) {
> +                     r = -ENOMEM;
> +                     goto out;
> +             }
>       }
>  
>       s->program_id = kstrdup(program_id, GFP_KERNEL);
> @@ -355,7 +381,7 @@ static int dm_stats_create(struct dm_stats *stats, 
> sector_t start, sector_t end,
>               cond_resched();
>       }
>  
> -     if (s->n_histogram_entries) {
> +     if (s->histogram_buckets) {
>               unsigned long long *hi;
>  
>               hi = dm_kvzalloc(s->histogram_alloc_size, NUMA_NO_NODE);
> @@ -365,7 +391,7 @@ static int dm_stats_create(struct dm_stats *stats, 
> sector_t start, sector_t end,
>               }
>               for (ni = 0; ni < n_entries; ni++) {
>                       s->stat_shared[ni].tmp.histogram = hi;
> -                     hi += s->n_histogram_entries + 1;
> +                     hi += s->histogram_buckets;
>                       cond_resched();
>               }
>       }
> @@ -377,7 +403,7 @@ static int dm_stats_create(struct dm_stats *stats, 
> sector_t start, sector_t end,
>                       goto out;
>               }
>               s->stat_percpu[cpu] = p;
> -             if (s->n_histogram_entries) {
> +             if (s->histogram_buckets) {
>                       unsigned long long *hi;
>  
>                       hi = dm_kvzalloc(s->histogram_alloc_size, 
> cpu_to_node(cpu));
> @@ -387,7 +413,7 @@ static int dm_stats_create(struct dm_stats *stats, 
> sector_t start, sector_t end,
>                       }
>                       for (ni = 0; ni < n_entries; ni++) {
>                               p[ni].histogram = hi;
> -                             hi += s->n_histogram_entries + 1;
> +                             hi += s->histogram_buckets;
>                               cond_resched();
>                       }
>               }
> @@ -498,6 +524,7 @@ static int dm_stats_list(struct dm_stats *stats, const 
> char *program,
>       struct dm_stat *s;
>       sector_t len;
>       unsigned int sz = 0;
> +     unsigned int hn;
>  
>       /*
>        * Output format:
> @@ -518,14 +545,15 @@ static int dm_stats_list(struct dm_stats *stats, const 
> char *program,
>                               DMEMIT(" precise_timestamps");
>                       if (s->stat_flags & STAT_HIST_TOTAL_LATENCY)
>                               DMEMIT(" hist_total_latency");
> -                     if (s->n_histogram_entries) {
> +                     for (hn = 0; hn < s->n_histograms; hn++) {
> +                             struct dm_stat_histogram *h = 
> &s->histograms[hn];
>                               unsigned int i;
>  
>                               DMEMIT(" histogram:");
> -                             for (i = 0; i < s->n_histogram_entries; i++) {
> +                             for (i = 0; i < h->n_entries; i++) {
>                                       if (i)
>                                               DMEMIT(",");
> -                                     DMEMIT("%llu", 
> s->histogram_boundaries[i]);
> +                                     DMEMIT("%llu", h->boundaries[i]);
>                               }
>                       }
>                       DMEMIT("\n");
> @@ -568,6 +596,30 @@ static void dm_stat_round(struct dm_stat *s, struct 
> dm_stat_shared *shared,
>       shared->stamp = now;
>  }
>  
> +
> +static void dm_stats_bin_duration(struct dm_stat *s, struct dm_stat_percpu 
> *p,
> +                               unsigned long long duration)
> +{
> +     unsigned int hn;
> +
> +
> +     for (hn = 0; hn < s->n_histograms; hn++) {
> +             struct dm_stat_histogram *h = &s->histograms[hn];
> +             unsigned int lo = 0, hi = h->n_entries + 1;
> +
> +             while (lo + 1 < hi) {
> +                     unsigned int mid = (lo + hi) / 2;
> +
> +                     if (h->boundaries[mid - 1] > duration)
> +                             hi = mid;
> +                     else
> +                             lo = mid;
> +             }
> +             p->histogram[h->offset + lo]++;
> +     }
> +}

I feel like this binary search is over complicated with all +1/-1 magic.

Maybe (note: I made it on top of the series):

@@ -655,17 +655,17 @@ static void dm_stats_bin_duration(struct dm_stat *s, 
struct dm_stat_percpu *p,
 {
        do {
                struct dm_stat_histogram *h = &s->histograms[__ffs(hmask)];
-               unsigned int lo = 0, hi = h->n_entries + 1;
+               unsigned int lo = 0, hi = h->n_entries;
 
                hmask &= hmask - 1;
 
-               while (lo + 1 < hi) {
+               while (lo < hi) {
                        unsigned int mid = (lo + hi) / 2;
 
-                       if (h->boundaries[mid - 1] > duration)
+                       if (h->boundaries[mid] > duration)
                                hi = mid;
                        else
-                               lo = mid;
+                               lo = mid + 1;
                }
                p->histogram[h->offset + lo]++;
        } while (hmask);

Should probably be an equivalent.

But now I see it comes from upstream, so probably we can leave as is.


> +
> +
>  static void dm_stat_for_entry(struct dm_stat *s, size_t entry,
>                             blk_opf_t bi_opf, sector_t len,
>                             struct dm_stats_aux *stats_aux, bool end,
> @@ -619,22 +671,12 @@ static void dm_stat_for_entry(struct dm_stat *s, size_t 
> entry,
>                       p->ticks[idx] += stats_aux->duration_ns;
>                       duration = stats_aux->duration_ns;
>               }
> -             if (s->n_histogram_entries) {
> -                     unsigned int lo = 0, hi = s->n_histogram_entries + 1;
> -
> +             if (s->n_histograms) {
>                       if ((s->stat_flags & STAT_HIST_TOTAL_LATENCY) &&
>                           stats_aux->histogram_duration_ns)
>                               duration = stats_aux->histogram_duration_ns;
>  
> -                     while (lo + 1 < hi) {
> -                             unsigned int mid = (lo + hi) / 2;
> -
> -                             if (s->histogram_boundaries[mid - 1] > duration)
> -                                     hi = mid;
> -                             else
> -                                     lo = mid;
> -                     }
> -                     p->histogram[lo]++;
> +                     dm_stats_bin_duration(s, p, duration);
>               }
>       }
>  
> @@ -756,8 +798,8 @@ static void __dm_stat_init_temporary_percpu_totals(struct 
> dm_stat_shared *shared
>       shared->tmp.io_ticks_total = 0;
>       shared->tmp.time_in_queue = 0;
>  
> -     if (s->n_histogram_entries)
> -             memset(shared->tmp.histogram, 0, (s->n_histogram_entries + 1) * 
> sizeof(unsigned long long));
> +     if (s->histogram_buckets)
> +             memset(shared->tmp.histogram, 0, s->histogram_buckets * 
> sizeof(unsigned long long));
>  
>       for_each_possible_cpu(cpu) {
>               p = &s->stat_percpu[cpu][x];
> @@ -773,10 +815,10 @@ static void 
> __dm_stat_init_temporary_percpu_totals(struct dm_stat_shared *shared
>               shared->tmp.io_ticks[WRITE] += READ_ONCE(p->io_ticks[WRITE]);
>               shared->tmp.io_ticks_total += READ_ONCE(p->io_ticks_total);
>               shared->tmp.time_in_queue += READ_ONCE(p->time_in_queue);
> -             if (s->n_histogram_entries) {
> +             if (s->histogram_buckets) {
>                       unsigned int i;
>  
> -                     for (i = 0; i < s->n_histogram_entries + 1; i++)
> +                     for (i = 0; i < s->histogram_buckets; i++)
>                               shared->tmp.histogram[i] += 
> READ_ONCE(p->histogram[i]);
>               }
>       }
> @@ -808,10 +850,10 @@ static void __dm_stat_clear(struct dm_stat *s, size_t 
> idx_start, size_t idx_end,
>               p->io_ticks_total -= shared->tmp.io_ticks_total;
>               p->time_in_queue -= shared->tmp.time_in_queue;
>               local_irq_enable();
> -             if (s->n_histogram_entries) {
> +             if (s->histogram_buckets) {
>                       unsigned int i;
>  
> -                     for (i = 0; i < s->n_histogram_entries + 1; i++) {
> +                     for (i = 0; i < s->histogram_buckets; i++) {
>                               local_irq_disable();
>                               p = &s->stat_percpu[smp_processor_id()][x];
>                               p->histogram[i] -= shared->tmp.histogram[i];
> @@ -874,6 +916,7 @@ static int dm_stats_print(struct dm_stats *stats, int id,
>       size_t x;
>       sector_t start, end, step;
>       size_t idx_end;
> +     unsigned int hn;
>       struct dm_stat_shared *shared;
>  
>       /*
> @@ -924,11 +967,13 @@ static int dm_stats_print(struct dm_stats *stats, int 
> id,
>                      dm_jiffies_to_msec64(s, shared->tmp.time_in_queue),
>                      dm_jiffies_to_msec64(s, shared->tmp.io_ticks[READ]),
>                      dm_jiffies_to_msec64(s, shared->tmp.io_ticks[WRITE]));
> -             if (s->n_histogram_entries) {
> +             for (hn = 0; hn < s->n_histograms; hn++) {
> +                     struct dm_stat_histogram *h = &s->histograms[hn];
>                       unsigned int i;
>  
> -                     for (i = 0; i < s->n_histogram_entries + 1; i++)
> -                             DMEMIT("%s%llu", !i ? " " : ":", 
> shared->tmp.histogram[i]);
> +                     for (i = 0; i < h->n_entries + 1; i++)
> +                             DMEMIT("%s%llu", !i ? " " : ":",
> +                                    shared->tmp.histogram[h->offset + i]);
>               }
>               DMEMIT("\n");
>  
> @@ -1013,6 +1058,21 @@ static int parse_histogram(const char *h, unsigned int 
> *n_histogram_entries,
>       }
>  }
>  
> +static int add_histogram(struct dm_stat_histogram *histograms,
> +                      unsigned int *n_histograms, const char *h)
> +{
> +     unsigned int idx;
> +
> +     if (*n_histograms >= DM_STAT_MAX_HISTOGRAMS)
> +             return -EINVAL;
> +
> +     idx = (*n_histograms)++;
> +     histograms[idx].boundaries = NULL;
> +
> +     return parse_histogram(h, &histograms[idx].n_entries,
> +                            &histograms[idx].boundaries);
> +}
> +
>  static int message_stats_create(struct mapped_device *md,
>                               unsigned int argc, char **argv,
>                               char *result, unsigned int maxlen)
> @@ -1024,11 +1084,12 @@ static int message_stats_create(struct mapped_device 
> *md,
>       unsigned int divisor;
>       const char *program_id, *aux_data;
>       unsigned int stat_flags = 0;
> -     unsigned int n_histogram_entries = 0;
> -     unsigned long long *histogram_boundaries = NULL;
> +     unsigned int n_histograms = 0;
> +     struct dm_stat_histogram histograms[DM_STAT_MAX_HISTOGRAMS];
>       struct dm_arg_set as, as_backup;
>       const char *a;
>       unsigned int feature_args;
> +     unsigned int i;
>  
>       /*
>        * Input format:
> @@ -1081,9 +1142,7 @@ static int message_stats_create(struct mapped_device 
> *md,
>                       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;
> -                             r = parse_histogram(a + 10, 
> &n_histogram_entries, &histogram_boundaries);
> +                             r = add_histogram(histograms, &n_histograms, a 
> + 10);
>                               if (r)
>                                       goto ret;
>                       } else
> @@ -1120,7 +1179,7 @@ static int message_stats_create(struct mapped_device 
> *md,
>       }
>  
>       id = dm_stats_create(dm_get_stats(md), start, end, step, stat_flags,
> -                          n_histogram_entries, histogram_boundaries, 
> program_id, aux_data,
> +                          n_histograms, histograms, program_id, aux_data,
>                            dm_internal_suspend_fast, dm_internal_resume_fast, 
> md);
>       if (id < 0) {
>               r = id;
> @@ -1135,7 +1194,8 @@ static int message_stats_create(struct mapped_device 
> *md,
>  ret_einval:
>       r = -EINVAL;
>  ret:
> -     kfree(histogram_boundaries);
> +     for (i = 0; i < n_histograms; i++)
> +             kfree(histograms[i].boundaries);
>       return r;
>  }
>  

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

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

Reply via email to