[v2,2/7] histogram: add helper function to expose histograms to userspace
diff mbox series

Message ID 20200528235253.75570-1-axelrasmussen@google.com
State New
Headers show
Series
  • Add histogram measuring mmap_lock contention latency
Related show

Commit Message

Axel Rasmussen May 28, 2020, 11:52 p.m. UTC
This change introduces the histogram_print_buckets_rcu(). This API is
used for implementing e.g. procfs or sysfs files exposing histograms.
print_buckets can be combined with e.g. seq_file to provide a read
interface for such a file.

This is a squashed, refactored, and modified version of a
previously-internal implementation. Thanks to the following individuals
for portions of the implementation:

  Junaid Shahid <junaids@google.com> - Original implementation
  Yu Zhao <yuzhao@google.com> - Simplification

Signed-off-by: Axel Rasmussen <axelrasmussen@google.com>
---
 include/linux/histogram.h | 23 ++++++++++++++++
 lib/histogram.c           | 55 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 78 insertions(+)

Patch
diff mbox series

diff --git a/include/linux/histogram.h b/include/linux/histogram.h
index 137930ca933f..7a1dc33fff5e 100644
--- a/include/linux/histogram.h
+++ b/include/linux/histogram.h
@@ -65,6 +65,12 @@ 
  * histogram_destroy_rcu(&hrcu);
  */
 
+/*
+ * Max number of digits in decimal needed to represent U64_MAX threshold,
+ * plus one space.
+ */
+#define HISTO_MAX_CHARS_PER_THRESHOLD (20 + 1)
+
 struct histogram {
 	struct rcu_head rcu;
 	u64 __percpu *buckets;
@@ -189,6 +195,23 @@  static inline void histogram_record_rcu(struct histogram_rcu *hrcu, u64 val,
 ssize_t histogram_read_rcu(const struct histogram_rcu *hrcu, u64 *buckets,
 			   u64 *thresholds, size_t nr_thresholds);
 
+/**
+ * histogram_print_buckets_rcu() - helper function to print histogram buckets
+ * @hrcu: histogram
+ * @buffer: output buffer to fill
+ * @len: length of the output buffer
+ *
+ * Reads buckets by calling histogram_read_rcu(), then fills the output buffer.
+ *
+ * Context: Performs allocation with GFP_ATOMIC.
+ *
+ * Returns: The number of characters written to @buffer, or a negative error
+ * code on failure. If the buffer isn't large enough to contain the output,
+ * -EINVAL is returned.
+ */
+int histogram_print_buckets_rcu(struct histogram_rcu *hrcu, char *buffer,
+				int len);
+
 /**
  * histogram_set_thresholds_rcu() - set RCU-protected histogram thresholds
  * @hrcu: histogram
diff --git a/lib/histogram.c b/lib/histogram.c
index b68334275a46..1a23590fc6e9 100644
--- a/lib/histogram.c
+++ b/lib/histogram.c
@@ -1,6 +1,7 @@ 
 // SPDX-License-Identifier: GPL-2.0
 
 #include <linux/cache.h>
+#include <linux/ctype.h>
 #include <linux/cpumask.h>
 #include <linux/err.h>
 #include <linux/export.h>
@@ -133,6 +134,60 @@  ssize_t histogram_read_rcu(const struct histogram_rcu *hrcu, u64 *buckets,
 }
 EXPORT_SYMBOL_GPL(histogram_read_rcu);
 
+int histogram_print_buckets_rcu(struct histogram_rcu *hrcu, char *buffer,
+				int len)
+{
+	const struct histogram *hist;
+	u64 *buckets = NULL;
+	u64 lower = 0;
+	size_t nr_buckets;
+	size_t i;
+	int ret;
+	int remaining = len;
+
+	rcu_read_lock_sched();
+	hist = rcu_dereference_sched(hrcu->hist);
+
+	nr_buckets = hist->nr_thresholds;
+	if (!nr_buckets) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	buckets = kmalloc_array(nr_buckets, sizeof(*hist->buckets), GFP_ATOMIC);
+	if (!buckets) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	histogram_read_buckets(hist, buckets);
+
+	for (i = 0; i < nr_buckets; i++) {
+		if (i == nr_buckets - 1)
+			ret = snprintf(buffer, remaining, "%llu-inf %llu\n",
+				       lower, buckets[i]);
+		else
+			ret = snprintf(buffer, remaining, "%llu-%llu %llu\n",
+				       lower, hist->thresholds[i], buckets[i]);
+		if (ret >= remaining) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+		buffer += ret;
+		remaining -= ret;
+
+		lower = hist->thresholds[i] + 1;
+	}
+
+	ret = len - remaining;
+out:
+	rcu_read_unlock_sched();
+	kfree(buckets);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(histogram_print_buckets_rcu);
+
 int histogram_set_thresholds_rcu(struct histogram_rcu *hrcu,
 				 const u64 *thresholds, size_t nr_thresholds)
 {