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

Andrey Zhadchenko andrey.zhadchenko at virtuozzo.com
Mon Jul 27 19:32:48 MSK 2026


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 | 159 ++++++++++++++++++++++++++++--------------
 1 file changed, 108 insertions(+), 51 deletions(-)

diff --git a/drivers/md/dm-stats.c b/drivers/md/dm-stats.c
index 49e19c413ce13..b0b6124124e2a 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) {
@@ -263,8 +274,8 @@ static void dm_stats_recalc_flags(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 *),
@@ -277,6 +288,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;
@@ -285,6 +298,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++;
@@ -303,15 +319,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 +
@@ -331,12 +349,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);
@@ -356,7 +382,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);
@@ -366,7 +392,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();
 		}
 	}
@@ -378,7 +404,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));
@@ -388,7 +414,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();
 			}
 		}
@@ -499,6 +525,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:
@@ -519,14 +546,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");
@@ -569,6 +597,28 @@ 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]++;
+	}
+}
+
 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,
@@ -624,19 +674,8 @@ static void dm_stat_for_entry(struct dm_stat *s, size_t entry,
 			else
 				duration = stats_aux->duration_ns;
 		}
-		if (s->n_histogram_entries) {
-			unsigned int lo = 0, hi = s->n_histogram_entries + 1;
-
-			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]++;
-		}
+		if (s->n_histograms)
+			dm_stats_bin_duration(s, p, duration);
 	}
 
 #if BITS_PER_LONG == 32
@@ -757,8 +796,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];
@@ -774,10 +813,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]);
 		}
 	}
@@ -809,10 +848,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];
@@ -875,6 +914,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;
 
 	/*
@@ -925,11 +965,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");
 
@@ -1014,6 +1056,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)
@@ -1025,11 +1082,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:
@@ -1082,9 +1140,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
@@ -1121,7 +1177,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;
@@ -1136,7 +1192,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;
 }
 
-- 
2.43.5



More information about the Devel mailing list