diff mbox series

[RFC,3/3] btrfs: perf: Add RO sysfs interface to collect perf result

Message ID 20190306061907.29685-4-wqu@suse.com (mailing list archive)
State New, archived
Headers show
Series btrfs: Performance profiler support | expand

Commit Message

Qu Wenruo March 6, 2019, 6:19 a.m. UTC
This patch adds a new sys fs interface, 'profiler', for user to get
real time performance data.

The content of /sys/fs/btrfs/<FDID>/profiler is generated at the time of
read, so user could have full control of the duration resolution.

The output example would be:
timestamp = 16364075995092ns
duration = 1025342515ns
TREE_LOCK_FS = 417160165ns (40.68%)
TREE_LOCK_EXTENT = 45087670ns (4.39%)
TREE_LOCK_ROOT = 1555506ns (0.15%)
TREE_LOCK_OTHER = 20387436ns (1.98%)

The 'timestamp' content is directly from ktime_get_ns(), which starts from
kernel boot, and doesn't count hibernation/suspension.
The 'duration' content is the time difference between last sample, in
nanoseconds. Doesn't count hibernation or suspension either.

The 'TREE_LOCK_*' content is the time spent on sleepable tree lock.
The percentage in the round brackets is the sleep time compared to
duration, which can be larger than 100%.

Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 fs/btrfs/perf.c  | 20 ++++++++++++++++++++
 fs/btrfs/perf.h  |  9 +++++++++
 fs/btrfs/sysfs.c | 39 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 68 insertions(+)
diff mbox series

Patch

diff --git a/fs/btrfs/perf.c b/fs/btrfs/perf.c
index 893bfad8e6d3..d1c11b91eeb8 100644
--- a/fs/btrfs/perf.c
+++ b/fs/btrfs/perf.c
@@ -70,3 +70,23 @@  void btrfs_perf_end(struct btrfs_fs_info *fs_info, u64 eb_owner, u64 start_ns)
 		i = BTRFS_PERF_TREE_LOCK_OTHER;
 	percpu_counter_add(&profiler->perf_counters[i], end_ns - start_ns);
 }
+
+void btrfs_perf_report(struct btrfs_fs_info *fs_info,
+		       struct btrfs_perf_result *result)
+{
+	struct btrfs_perf_profiler *profiler = fs_info->profiler;
+	u64 end_ns = ktime_get_ns();
+	int i;
+
+	if (!profiler || !result)
+		return;
+
+	for (i = 0; i < BTRFS_PERF_LAST; i++) {
+		result->results_ns[i] =
+			percpu_counter_sum(&profiler->perf_counters[i]);
+		percpu_counter_set(&profiler->perf_counters[i], 0);
+	}
+	result->current_ns = end_ns;
+	result->duration_ns = end_ns - profiler->last_sample;
+	profiler->last_sample = end_ns;
+}
diff --git a/fs/btrfs/perf.h b/fs/btrfs/perf.h
index 7cf4b8c9a0ad..0af12aa148b4 100644
--- a/fs/btrfs/perf.h
+++ b/fs/btrfs/perf.h
@@ -22,6 +22,12 @@  struct btrfs_perf_profiler {
 	struct percpu_counter perf_counters[BTRFS_PERF_LAST];
 };
 
+struct btrfs_perf_result {
+	u64 current_ns;
+	u64 duration_ns;
+	u64 results_ns[BTRFS_PERF_LAST];
+};
+
 struct btrfs_perf_profiler *btrfs_perf_alloc_profiler(void);
 void btrfs_perf_free_profiler(struct btrfs_fs_info *fs_info);
 void btrfs_perf_update_lock(struct btrfs_fs_info *fs_info,
@@ -32,4 +38,7 @@  static inline u64 btrfs_perf_start(void)
 }
 
 void btrfs_perf_end(struct btrfs_fs_info *fs_info, u64 eb_owner, u64 start_ns);
+
+void btrfs_perf_report(struct btrfs_fs_info *fs_info,
+		       struct btrfs_perf_result *result);
 #endif
diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c
index 5a5930e3d32b..2b5d72b699d8 100644
--- a/fs/btrfs/sysfs.c
+++ b/fs/btrfs/sysfs.c
@@ -16,6 +16,7 @@ 
 #include "transaction.h"
 #include "sysfs.h"
 #include "volumes.h"
+#include "perf.h"
 
 static inline struct btrfs_fs_info *to_fs_info(struct kobject *kobj);
 static inline struct btrfs_fs_devices *to_fs_devs(struct kobject *kobj);
@@ -456,6 +457,43 @@  static ssize_t btrfs_sectorsize_show(struct kobject *kobj,
 
 BTRFS_ATTR(, sectorsize, btrfs_sectorsize_show);
 
+#define print_one_result(name)					\
+({								\
+	tmp = result.results_ns[BTRFS_PERF_##name];		\
+	size += snprintf(buf + size, PAGE_SIZE - size,		\
+			 "%s = %lluns (%llu.%02llu%%)\n",	\
+			 #name, tmp, tmp * 100 / duration,	\
+			 (tmp * 10000 / duration) % 100);	\
+})
+
+static ssize_t btrfs_profiler_show(struct kobject *kobj,
+				struct kobj_attribute *a, char *buf)
+{
+	struct btrfs_fs_info *fs_info = to_fs_info(kobj);
+	struct btrfs_perf_result result = { 0 };
+	u64 tmp;
+	u64 duration;
+	ssize_t size = 0;
+
+	btrfs_perf_report(fs_info, &result);
+	if (result.duration_ns == 0)
+		return snprintf(buf, PAGE_SIZE, "profiler not running\n");
+
+	duration = result.duration_ns;
+	size += snprintf(buf + size, PAGE_SIZE - size, "timestamp = %lluns\n",
+			 result.current_ns);
+	size += snprintf(buf + size, PAGE_SIZE - size, "duration = %lluns\n",
+			 duration);
+	print_one_result(TREE_LOCK_FS);
+	print_one_result(TREE_LOCK_EXTENT);
+	print_one_result(TREE_LOCK_ROOT);
+	print_one_result(TREE_LOCK_OTHER);
+	return size;
+}
+#undef print_one_result
+
+BTRFS_ATTR(, profiler, btrfs_profiler_show);
+
 static ssize_t btrfs_clone_alignment_show(struct kobject *kobj,
 				struct kobj_attribute *a, char *buf)
 {
@@ -525,6 +563,7 @@  static const struct attribute *btrfs_attrs[] = {
 	BTRFS_ATTR_PTR(, clone_alignment),
 	BTRFS_ATTR_PTR(, quota_override),
 	BTRFS_ATTR_PTR(, metadata_uuid),
+	BTRFS_ATTR_PTR(, profiler),
 	NULL,
 };