@@ -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:
/*
@@ -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))
@@ -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;
}
@@ -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 */
@@ -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>
@@ -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__ */