[Devel] [PATCH VZ10 05/11] drivers/md/dm-stats: prepare for multiple histograms per region

Vasileios Almpanis vasileios.almpanis at virtuozzo.com
Tue Jul 14 13:40:30 MSK 2026


On 7/13/26 2:36 AM, 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 <andrey.zhadchenko at virtuozzo.com>
> ---
>   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;
> +
> +
multiple blank line here
> +	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]++;
> +	}
> +}
> +
multiple blank line here
> +
>   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, Vasileios Almpanis
Software Developer, Virtuozzo.



More information about the Devel mailing list