]> www.infradead.org Git - users/hch/xfs.git/commitdiff
xfs: add data placement info to mount stats
authorHans Holmberg <hans.holmberg@wdc.com>
Sun, 6 Oct 2024 05:04:42 +0000 (07:04 +0200)
committerChristoph Hellwig <hch@lst.de>
Tue, 5 Nov 2024 08:29:54 +0000 (09:29 +0100)
Add per-rtg active refs, life time hint and data separation score and
an aggregate data separation score as output to the mount stats
to aid debugging and analysis.

Signed-off-by: Hans Holmberg <hans.holmberg@wdc.com>
fs/xfs/xfs_zone_alloc.c

index 332d01c3a65a623b80ebb3ab47f6d6d4cfd960b1..29dea65fcd5735393261f178b164baf868132593 100644 (file)
@@ -579,15 +579,87 @@ xfs_zone_finish_alloc(
                xfs_rtgroup_rele(rtg);
 }
 
+/* XXX Should probably go in as a helper somewhere else */
+static const char xfs_write_hint_shorthand[6][16] = {
+       "NOT_SET", "NONE", "SHORT", "MEDIUM", "LONG", "EXTREME"};
+
+static inline const char *
+xfs_write_hint_to_str(
+       uint8_t                 write_hint)
+{
+       if (write_hint > WRITE_LIFE_EXTREME)
+               return "UNKNOWN";
+
+       return xfs_write_hint_shorthand[write_hint];
+}
+
+/*
+ * Calculate a simple data placement score based on utilization
+ */
+static unsigned long long
+xfs_zone_data_separation_score(
+       struct xfs_rtgroup      *rtg)
+{
+       unsigned long long d;
+
+       /*
+        * The data separation score does not really
+        * make much sense until we've filled the zone
+        * so return 50 (~ neither here nor there)
+        */
+
+       if (rtg->rtg_write_pointer != rtg->rtg_extents)
+               return 50;
+
+       /*
+        * Data placement was good if the zone is nearly full (high utilization)
+        * nearly empty (cheap to reclaim), so calculate a composite score based on
+        * distance to utilization "mid point"
+        */
+       d = abs((signed long long)(*xfs_zone_used_counter(rtg)) -
+               (signed long long)(rtg->rtg_extents >> 1));
+
+       /* (100 * d) / (rtg->rtg_extents / 2) */
+       return div64_ul(2 * 100 * d, rtg->rtg_extents);
+}
+
+static unsigned long long
+xfs_average_zone_data_separation_score(
+       struct xfs_mount        *mp)
+{
+       struct xfs_rtgroup      *rtg;
+       unsigned long           index;
+       unsigned long long      sum = 0;
+       unsigned long long      count = 0;
+
+       xa_for_each_marked(&mp->m_groups[XG_TYPE_RTG].xa, index, rtg,
+                       XFS_RTG_RECLAIMABLE) {
+               sum += xfs_zone_data_separation_score(rtg);
+               count++;
+       }
+
+       /*
+        * If we have no reclaimable zones, we probably did a good data
+        * placement job, so return the optimal number (100%)
+        */
+       if (count == 0)
+               return 100;
+
+       return div64_long(sum, count);
+}
+
 static void
 xfs_show_zone(
        struct seq_file         *m,
        struct xfs_rtgroup      *rtg)
 {
-       seq_printf(m, "\t  zone %d, wp %u, written %u, used %llu\n",
+       seq_printf(m, "\t  zone %d, wp %u, written %u, used %llu, hint %s, active %u dss %llu\n",
                rtg_rgno(rtg),
                rtg->rtg_write_pointer, rtg->rtg_written,
-               *xfs_zone_used_counter(rtg));
+               *xfs_zone_used_counter(rtg),
+               xfs_write_hint_to_str(rtg->rtg_write_hint),
+               atomic_read(&rtg->rtg_group.xg_active_ref),
+               xfs_zone_data_separation_score(rtg));
 }
 
 void
@@ -613,6 +685,8 @@ xfs_zoned_show_stats(
                !list_empty_careful(&mp->m_reclaim_reservations));
        seq_printf(m, "\tGC required: %d\n",
                xfs_zoned_need_gc(mp));
+       seq_printf(m, "\tavg data separation score: %llu\n",
+               xfs_average_zone_data_separation_score(mp));
 
        spin_lock(&mp->m_zone_list_lock);
        seq_printf(m, "\tfree zones: %d\n", atomic_read(&mp->m_nr_free_zones));