From: Darrick J. Wong <djwong@xxxxxxxxxx> Use the timestats code to report statistical information about how much time we spend in scrub and repair. This augments the existing raw scrub counters. Signed-off-by: Darrick J. Wong <djwong@xxxxxxxxxx> --- fs/xfs/scrub/repair.c | 6 +- fs/xfs/scrub/scrub.c | 6 +- fs/xfs/scrub/stats.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++- fs/xfs/scrub/stats.h | 21 +------- fs/xfs/xfs_linux.h | 1 fs/xfs/xfs_timestats.h | 2 + 6 files changed, 137 insertions(+), 27 deletions(-) diff --git a/fs/xfs/scrub/repair.c b/fs/xfs/scrub/repair.c index 77db28830ce9e..81955d0a188cf 100644 --- a/fs/xfs/scrub/repair.c +++ b/fs/xfs/scrub/repair.c @@ -42,6 +42,7 @@ #include "xfs_rtalloc.h" #include "xfs_imeta.h" #include "xfs_rtrefcount_btree.h" +#include "xfs_timestats.h" #include "scrub/scrub.h" #include "scrub/common.h" #include "scrub/trace.h" @@ -61,7 +62,6 @@ xrep_attempt( struct xfs_scrub *sc, struct xchk_stats_run *run) { - u64 repair_start; int error = 0; trace_xrep_attempt(XFS_I(file_inode(sc->file)), sc->sm, error); @@ -72,10 +72,10 @@ xrep_attempt( /* Repair whatever's broken. */ ASSERT(sc->ops->repair); run->repair_attempted = true; - repair_start = xchk_stats_now(); + run->repair_start = xchk_stats_now(); error = sc->ops->repair(sc); + run->repair_stop = xchk_stats_now(); trace_xrep_done(XFS_I(file_inode(sc->file)), sc->sm, error); - run->repair_ns += xchk_stats_elapsed_ns(repair_start); switch (error) { case 0: /* diff --git a/fs/xfs/scrub/scrub.c b/fs/xfs/scrub/scrub.c index 4322743aa5578..fc4a71fab51e6 100644 --- a/fs/xfs/scrub/scrub.c +++ b/fs/xfs/scrub/scrub.c @@ -22,6 +22,7 @@ #include "xfs_dir2.h" #include "xfs_parent.h" #include "xfs_icache.h" +#include "xfs_timestats.h" #include "scrub/scrub.h" #include "scrub/common.h" #include "scrub/trace.h" @@ -677,7 +678,6 @@ xfs_scrub_metadata( struct xchk_stats_run run = { }; struct xfs_scrub *sc; struct xfs_mount *mp = XFS_I(file_inode(file))->i_mount; - u64 check_start; int error = 0; BUILD_BUG_ON(sizeof(meta_scrub_ops) != @@ -735,12 +735,12 @@ xfs_scrub_metadata( goto out_teardown; /* Scrub for errors. */ - check_start = xchk_stats_now(); + run.scrub_start = xchk_stats_now(); if ((sc->flags & XREP_ALREADY_FIXED) && sc->ops->repair_eval != NULL) error = sc->ops->repair_eval(sc); else error = sc->ops->scrub(sc); - run.scrub_ns += xchk_stats_elapsed_ns(check_start); + run.scrub_stop = xchk_stats_now(); if (error == -EDEADLOCK && !(sc->flags & XCHK_TRY_HARDER)) goto try_harder; if (error == -ECHRNG && !(sc->flags & XCHK_NEED_DRAIN)) diff --git a/fs/xfs/scrub/stats.c b/fs/xfs/scrub/stats.c index 0e0be23adfcb4..b9e6ace59e572 100644 --- a/fs/xfs/scrub/stats.c +++ b/fs/xfs/scrub/stats.c @@ -12,6 +12,7 @@ #include "xfs_sysfs.h" #include "xfs_btree.h" #include "xfs_super.h" +#include "xfs_timestats.h" #include "scrub/scrub.h" #include "scrub/stats.h" #include "scrub/trace.h" @@ -44,12 +45,24 @@ struct xchk_scrub_stats { spinlock_t css_lock; }; +struct xchk_timestats { +#ifdef CONFIG_XFS_TIME_STATS + struct dentry *parent; + struct { + struct time_stats scrub; + struct time_stats repair; + } scrub[XFS_SCRUB_TYPE_NR]; +#endif +}; + struct xchk_stats { struct dentry *cs_debugfs; +#ifdef CONFIG_XFS_TIME_STATS + struct xchk_timestats *cs_timestats; +#endif struct xchk_scrub_stats cs_stats[XFS_SCRUB_TYPE_NR]; }; - static struct xchk_stats global_stats; static const char *name_map[XFS_SCRUB_TYPE_NR] = { @@ -86,6 +99,107 @@ static const char *name_map[XFS_SCRUB_TYPE_NR] = { [XFS_SCRUB_TYPE_RTRMAPBT] = "rtrmapbt", [XFS_SCRUB_TYPE_RTREFCBT] = "rtrefcountbt", }; +#ifdef CONFIG_XFS_TIME_STATS +static inline void +xchk_timestats_init( + struct xchk_stats *cs, + struct xfs_mount *mp) +{ + struct xchk_timestats *ts; + unsigned int i; + + /* Only individual mounts have timestats so far */ + if (!mp) { + cs->cs_timestats = NULL; + return; + } + + /* timestats are optional */ + ts = kmalloc(sizeof(struct xchk_timestats), GFP_KERNEL); + if (!ts) { + cs->cs_timestats = NULL; + return; + } + + for (i = 0; i < XFS_SCRUB_TYPE_NR; i++) { + time_stats_init(&ts->scrub[i].scrub); + time_stats_init(&ts->scrub[i].repair); + } + + ts->parent = mp->m_timestats.ts_debugfs; + cs->cs_timestats = ts; +} + +static inline void +xchk_timestats_teardown( + struct xchk_stats *cs) +{ + struct xchk_timestats *ts = cs->cs_timestats; + unsigned int i; + + if (!ts) + return; + + for (i = 0; i < XFS_SCRUB_TYPE_NR; i++) { + time_stats_exit(&ts->scrub[i].scrub); + time_stats_exit(&ts->scrub[i].repair); + } + kfree(ts); + cs->cs_timestats = NULL; +} + +static inline void +xchk_timestats_register( + struct xchk_stats *cs) +{ + char name[32]; + struct xchk_timestats *ts = cs->cs_timestats; + unsigned int i; + + if (!ts) + return; + + for (i = 0; i < XFS_SCRUB_TYPE_NR; i++) { + if (!name_map[i]) + continue; + + snprintf(name, 32, "scrub::%s", name_map[i]); + debugfs_create_file(name, 0444, ts->parent, + &ts->scrub[i].scrub, &xfs_timestats_fops); + + snprintf(name, 32, "repair::%s", name_map[i]); + debugfs_create_file(name, 0444, ts->parent, + &ts->scrub[i].repair, &xfs_timestats_fops); + } +} + +STATIC void +xchk_timestats_merge_one( + struct xchk_stats *cs, + const struct xfs_scrub_metadata *sm, + const struct xchk_stats_run *run) +{ + struct xchk_timestats *ts = cs->cs_timestats; + + if (sm->sm_type >= XFS_SCRUB_TYPE_NR) { + ASSERT(sm->sm_type < XFS_SCRUB_TYPE_NR); + return; + } + if (!ts) + return; + + xfs_timestats_interval(&ts->scrub[sm->sm_type].scrub, + run->scrub_start, run->scrub_stop); + xfs_timestats_interval(&ts->scrub[sm->sm_type].repair, + run->repair_start, run->repair_stop); +} + +#else +# define xchk_timestats_init(cs, mp) ((void)0) +# define xchk_timestats_teardown(cs) ((void)0) +# define xchk_timestats_register(cs) ((void)0) +# define xchk_timestats_merge_one(...) ((void)0) +#endif /* Format the scrub stats into a text buffer, similar to pcp style. */ STATIC ssize_t @@ -192,6 +306,7 @@ xchk_stats_merge_one( const struct xchk_stats_run *run) { struct xchk_scrub_stats *css; + u64 delta; if (sm->sm_type >= XFS_SCRUB_TYPE_NR) { ASSERT(sm->sm_type < XFS_SCRUB_TYPE_NR); @@ -216,13 +331,15 @@ xchk_stats_merge_one( if (sm->sm_flags & XFS_SCRUB_OFLAG_WARNING) css->warning++; css->retries += run->retries; - css->checktime_us += howmany_64(run->scrub_ns, NSEC_PER_USEC); + delta = max(1, run->scrub_stop - run->scrub_start); + css->checktime_us += howmany_64(delta, NSEC_PER_USEC); if (run->repair_attempted) css->repair_invocations++; if (run->repair_succeeded) css->repair_success++; - css->repairtime_us += howmany_64(run->repair_ns, NSEC_PER_USEC); + delta = max(1, run->repair_stop - run->repair_start); + css->repairtime_us += howmany_64(delta, NSEC_PER_USEC); spin_unlock(&css->css_lock); } @@ -235,6 +352,7 @@ xchk_stats_merge( { xchk_stats_merge_one(&global_stats, sm, run); xchk_stats_merge_one(mp->m_scrub_stats, sm, run); + xchk_timestats_merge_one(mp->m_scrub_stats, sm, run); } /* debugfs boilerplate */ @@ -321,6 +439,7 @@ xchk_stats_init( for (i = 0; i < XFS_SCRUB_TYPE_NR; i++, css++) spin_lock_init(&css->css_lock); + xchk_timestats_init(cs, mp); return 0; } @@ -341,6 +460,8 @@ xchk_stats_register( &scrub_stats_fops); debugfs_create_file("clear_stats", 0400, cs->cs_debugfs, cs, &clear_scrub_stats_fops); + + xchk_timestats_register(cs); } /* Free all resources related to the stats object. */ @@ -348,6 +469,7 @@ STATIC int xchk_stats_teardown( struct xchk_stats *cs) { + xchk_timestats_teardown(cs); return 0; } diff --git a/fs/xfs/scrub/stats.h b/fs/xfs/scrub/stats.h index b358ad8d8b90a..f615bff22dd22 100644 --- a/fs/xfs/scrub/stats.h +++ b/fs/xfs/scrub/stats.h @@ -7,8 +7,8 @@ #define __XFS_SCRUB_STATS_H__ struct xchk_stats_run { - u64 scrub_ns; - u64 repair_ns; + u64 scrub_start, scrub_stop; + u64 repair_start, repair_stop; unsigned int retries; bool repair_attempted; bool repair_succeeded; @@ -29,21 +29,7 @@ void xchk_stats_unregister(struct xchk_stats *cs); void xchk_stats_merge(struct xfs_mount *mp, const struct xfs_scrub_metadata *sm, const struct xchk_stats_run *run); -static inline u64 xchk_stats_now(void) { return ktime_get_ns(); } -static inline u64 xchk_stats_elapsed_ns(u64 since) -{ - u64 now = xchk_stats_now(); - - /* - * If the system doesn't have a high enough resolution clock, charge at - * least one nanosecond so that our stats don't report instantaneous - * runtimes. - */ - if (now == since) - return 1; - - return now - since; -} +static inline u64 xchk_stats_now(void) { return local_clock(); } #else # define xchk_global_stats_setup(parent) (0) # define xchk_global_stats_teardown() ((void)0) @@ -52,7 +38,6 @@ static inline u64 xchk_stats_elapsed_ns(u64 since) # define xchk_stats_register(cs, parent) ((void)0) # define xchk_stats_unregister(cs) ((void)0) # define xchk_stats_now() (0) -# define xchk_stats_elapsed_ns(x) (0 * (x)) # define xchk_stats_merge(mp, sm, run) ((void)0) #endif /* CONFIG_XFS_ONLINE_SCRUB_STATS */ diff --git a/fs/xfs/xfs_linux.h b/fs/xfs/xfs_linux.h index 27f9ec7721a93..8598294514aa3 100644 --- a/fs/xfs/xfs_linux.h +++ b/fs/xfs/xfs_linux.h @@ -68,6 +68,7 @@ typedef __u32 xfs_nlink_t; # include <linux/seq_buf.h> # include <linux/time_stats.h> #endif +#include <linux/sched/clock.h> #include <asm/page.h> #include <asm/div64.h> diff --git a/fs/xfs/xfs_timestats.h b/fs/xfs/xfs_timestats.h index e53dbb40c8fff..418e5abf2cf12 100644 --- a/fs/xfs/xfs_timestats.h +++ b/fs/xfs/xfs_timestats.h @@ -18,6 +18,7 @@ void xfs_timestats_destroy(struct xfs_mount *mp); # define DEFINE_XFS_TIMESTAT(name) u64 name = local_clock() # define xfs_timestats_start(b) do { *(b) = local_clock(); } while (0) # define xfs_timestats_end(a, b) time_stats_update((a), (b)) +# define xfs_timestats_interval(a,b,c) __time_stats_update((a), (b), (c)) #else # define xfs_timestats_init(mp) ((void)0) # define xfs_timestats_export(mp) ((void)0) @@ -28,6 +29,7 @@ void xfs_timestats_destroy(struct xfs_mount *mp); # define DEFINE_XFS_TIMESTAT(name) # define xfs_timestats_start(t) ((void)0) # define xfs_timestats_end(s, t) ((void)0) +# define xfs_timestats_interval(...) ((void)0) #endif /* CONFIG_XFS_TIME_STATS */ #endif /* __XFS_TIMESTATS_H__ */