@@ -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
@@ -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)
{
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(+)