bcachefs: Add persistent counters
This adds a new superblock field for persisting counters and adds a sysfs interface in counters/ exposing these counters. The superblock field is ignored by older versions letting us avoid an on disk version bump. Each sysfs file outputs a counter that tracks since filesystem creation and a counter for the current mount session. Signed-off-by: Daniel Hill <daniel@gluo.nz> Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
parent
1f93726e63
commit
104c69745f
|
@ -21,6 +21,7 @@ bcachefs-y := \
|
|||
checksum.o \
|
||||
clock.o \
|
||||
compress.o \
|
||||
counters.o \
|
||||
debug.o \
|
||||
dirent.o \
|
||||
disk_groups.o \
|
||||
|
|
|
@ -584,6 +584,7 @@ struct bch_fs {
|
|||
|
||||
struct list_head list;
|
||||
struct kobject kobj;
|
||||
struct kobject counters_kobj;
|
||||
struct kobject internal;
|
||||
struct kobject opts_dir;
|
||||
struct kobject time_stats;
|
||||
|
@ -900,12 +901,15 @@ mempool_t bio_bounce_pages;
|
|||
|
||||
u64 last_bucket_seq_cleanup;
|
||||
|
||||
/* The rest of this all shows up in sysfs */
|
||||
/* TODO rewrite as counters - The rest of this all shows up in sysfs */
|
||||
atomic_long_t read_realloc_races;
|
||||
atomic_long_t extent_migrate_done;
|
||||
atomic_long_t extent_migrate_raced;
|
||||
atomic_long_t bucket_alloc_fail;
|
||||
|
||||
u64 counters_on_mount[BCH_COUNTER_NR];
|
||||
u64 __percpu *counters;
|
||||
|
||||
unsigned btree_gc_periodic:1;
|
||||
unsigned copy_gc_enabled:1;
|
||||
bool promote_whole_extents;
|
||||
|
|
|
@ -1090,7 +1090,8 @@ struct bch_sb_field {
|
|||
x(clean, 6) \
|
||||
x(replicas, 7) \
|
||||
x(journal_seq_blacklist, 8) \
|
||||
x(journal_v2, 9)
|
||||
x(journal_v2, 9) \
|
||||
x(counters, 10)
|
||||
|
||||
enum bch_sb_field_type {
|
||||
#define x(f, nr) BCH_SB_FIELD_##f = nr,
|
||||
|
@ -1323,6 +1324,25 @@ struct bch_sb_field_disk_groups {
|
|||
struct bch_disk_group entries[0];
|
||||
} __attribute__((packed, aligned(8)));
|
||||
|
||||
/* BCH_SB_FIELD_counters */
|
||||
|
||||
#define BCH_PERSISTENT_COUNTERS() \
|
||||
x(io_read, 0) \
|
||||
x(io_write, 1) \
|
||||
x(io_move, 2)
|
||||
|
||||
enum bch_persistent_counters {
|
||||
#define x(t, n, ...) BCH_COUNTER_##t,
|
||||
BCH_PERSISTENT_COUNTERS()
|
||||
#undef x
|
||||
BCH_COUNTER_NR
|
||||
};
|
||||
|
||||
struct bch_sb_field_counters {
|
||||
struct bch_sb_field field;
|
||||
__le64 d[0];
|
||||
};
|
||||
|
||||
/*
|
||||
* On clean shutdown, store btree roots and current journal sequence number in
|
||||
* the superblock:
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "bcachefs.h"
|
||||
#include "super-io.h"
|
||||
#include "counters.h"
|
||||
|
||||
/* BCH_SB_FIELD_counters */
|
||||
|
||||
const char * const bch2_counter_names[] = {
|
||||
#define x(t, n, ...) (#t),
|
||||
BCH_PERSISTENT_COUNTERS()
|
||||
#undef x
|
||||
NULL
|
||||
};
|
||||
|
||||
static size_t bch2_sb_counter_nr_entries(struct bch_sb_field_counters *ctrs)
|
||||
{
|
||||
if (!ctrs)
|
||||
return 0;
|
||||
|
||||
return (__le64 *) vstruct_end(&ctrs->field) - &ctrs->d[0];
|
||||
};
|
||||
|
||||
static int bch2_sb_counters_validate(struct bch_sb *sb,
|
||||
struct bch_sb_field *f,
|
||||
struct printbuf *err)
|
||||
{
|
||||
return 0;
|
||||
};
|
||||
|
||||
void bch2_sb_counters_to_text(struct printbuf *out, struct bch_sb *sb,
|
||||
struct bch_sb_field *f)
|
||||
{
|
||||
struct bch_sb_field_counters *ctrs = field_to_type(f, counters);
|
||||
unsigned int i;
|
||||
unsigned int nr = bch2_sb_counter_nr_entries(ctrs);
|
||||
|
||||
for (i = 0; i < nr; i++) {
|
||||
if (i < BCH_COUNTER_NR)
|
||||
pr_buf(out, "%s", bch2_counter_names[i]);
|
||||
else
|
||||
pr_buf(out, "(unknown)");
|
||||
|
||||
pr_tab(out);
|
||||
pr_buf(out, "%llu", le64_to_cpu(ctrs->d[i]));
|
||||
pr_newline(out);
|
||||
};
|
||||
};
|
||||
|
||||
int bch2_sb_counters_to_cpu(struct bch_fs *c)
|
||||
{
|
||||
struct bch_sb_field_counters *ctrs = bch2_sb_get_counters(c->disk_sb.sb);
|
||||
unsigned int i;
|
||||
unsigned int nr = bch2_sb_counter_nr_entries(ctrs);
|
||||
u64 val = 0;
|
||||
|
||||
for (i = 0; i < BCH_COUNTER_NR; i++)
|
||||
c->counters_on_mount[i] = 0;
|
||||
|
||||
for (i = 0; i < min_t(unsigned int, nr, BCH_COUNTER_NR); i++) {
|
||||
val = le64_to_cpu(ctrs->d[i]);
|
||||
percpu_u64_set(&c->counters[i], val);
|
||||
c->counters_on_mount[i] = val;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
int bch2_sb_counters_from_cpu(struct bch_fs *c)
|
||||
{
|
||||
struct bch_sb_field_counters *ctrs = bch2_sb_get_counters(c->disk_sb.sb);
|
||||
struct bch_sb_field_counters *ret;
|
||||
unsigned int i;
|
||||
unsigned int nr = bch2_sb_counter_nr_entries(ctrs);
|
||||
|
||||
if (nr < BCH_COUNTER_NR) {
|
||||
ret = bch2_sb_resize_counters(&c->disk_sb,
|
||||
sizeof(*ctrs) / sizeof(u64) + BCH_COUNTER_NR);
|
||||
|
||||
if (ret) {
|
||||
ctrs = ret;
|
||||
nr = bch2_sb_counter_nr_entries(ctrs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (i = 0; i < min_t(unsigned int, nr, BCH_COUNTER_NR); i++)
|
||||
ctrs->d[i] = cpu_to_le64(percpu_u64_get(&c->counters[i]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bch2_fs_counters_exit(struct bch_fs *c)
|
||||
{
|
||||
free_percpu(c->counters);
|
||||
}
|
||||
|
||||
int bch2_fs_counters_init(struct bch_fs *c)
|
||||
{
|
||||
c->counters = __alloc_percpu(sizeof(u64) * BCH_COUNTER_NR, sizeof(u64));
|
||||
if (!c->counters)
|
||||
return -ENOMEM;
|
||||
|
||||
return bch2_sb_counters_to_cpu(c);
|
||||
}
|
||||
|
||||
const struct bch_sb_field_ops bch_sb_field_ops_counters = {
|
||||
.validate = bch2_sb_counters_validate,
|
||||
.to_text = bch2_sb_counters_to_text,
|
||||
};
|
|
@ -0,0 +1,17 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _BCACHEFS_COUNTERS_H
|
||||
#define _BCACHEFS_COUNTERS_H
|
||||
|
||||
#include "bcachefs.h"
|
||||
#include "super-io.h"
|
||||
|
||||
|
||||
int bch2_sb_counters_to_cpu(struct bch_fs *);
|
||||
int bch2_sb_counters_from_cpu(struct bch_fs *);
|
||||
|
||||
void bch2_fs_counters_exit(struct bch_fs *);
|
||||
int bch2_fs_counters_init(struct bch_fs *);
|
||||
|
||||
extern const struct bch_sb_field_ops bch_sb_field_ops_counters;
|
||||
|
||||
#endif // _BCACHEFS_COUNTERS_H
|
|
@ -1403,6 +1403,7 @@ void bch2_write(struct closure *cl)
|
|||
goto err;
|
||||
}
|
||||
|
||||
this_cpu_add(c->counters[BCH_COUNTER_io_write], bio_sectors(bio));
|
||||
bch2_increment_clock(c, bio_sectors(bio), WRITE);
|
||||
|
||||
data_len = min_t(u64, bio->bi_iter.bi_size,
|
||||
|
@ -2310,6 +2311,7 @@ get_bio:
|
|||
if (rbio->bounce)
|
||||
trace_read_bounce(&rbio->bio);
|
||||
|
||||
this_cpu_add(c->counters[BCH_COUNTER_io_read], bio_sectors(&rbio->bio));
|
||||
bch2_increment_clock(c, bio_sectors(&rbio->bio), READ);
|
||||
|
||||
/*
|
||||
|
|
|
@ -575,6 +575,7 @@ static int bch2_move_extent(struct btree_trans *trans,
|
|||
|
||||
atomic64_inc(&ctxt->stats->keys_moved);
|
||||
atomic64_add(k.k->size, &ctxt->stats->sectors_moved);
|
||||
this_cpu_add(c->counters[BCH_COUNTER_io_move], k.k->size);
|
||||
|
||||
trace_move_extent(k.k);
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "super.h"
|
||||
#include "trace.h"
|
||||
#include "vstructs.h"
|
||||
#include "counters.h"
|
||||
|
||||
#include <linux/backing-dev.h>
|
||||
#include <linux/sort.h>
|
||||
|
@ -819,6 +820,8 @@ int bch2_write_super(struct bch_fs *c)
|
|||
|
||||
SET_BCH_SB_BIG_ENDIAN(c->disk_sb.sb, CPU_BIG_ENDIAN);
|
||||
|
||||
bch2_sb_counters_from_cpu(c);
|
||||
|
||||
for_each_online_member(ca, c, i)
|
||||
bch2_sb_from_fs(c, ca);
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "checksum.h"
|
||||
#include "clock.h"
|
||||
#include "compress.h"
|
||||
#include "counters.h"
|
||||
#include "debug.h"
|
||||
#include "disk_groups.h"
|
||||
#include "ec.h"
|
||||
|
@ -78,6 +79,9 @@ static const struct kobj_type type ## _ktype = { \
|
|||
|
||||
static void bch2_fs_release(struct kobject *);
|
||||
static void bch2_dev_release(struct kobject *);
|
||||
static void bch2_fs_counters_release(struct kobject *k)
|
||||
{
|
||||
}
|
||||
|
||||
static void bch2_fs_internal_release(struct kobject *k)
|
||||
{
|
||||
|
@ -92,6 +96,7 @@ static void bch2_fs_time_stats_release(struct kobject *k)
|
|||
}
|
||||
|
||||
KTYPE(bch2_fs);
|
||||
KTYPE(bch2_fs_counters);
|
||||
KTYPE(bch2_fs_internal);
|
||||
KTYPE(bch2_fs_opts_dir);
|
||||
KTYPE(bch2_fs_time_stats);
|
||||
|
@ -416,6 +421,7 @@ static void __bch2_fs_free(struct bch_fs *c)
|
|||
for (i = 0; i < BCH_TIME_STAT_NR; i++)
|
||||
bch2_time_stats_exit(&c->times[i]);
|
||||
|
||||
bch2_fs_counters_exit(c);
|
||||
bch2_fs_snapshots_exit(c);
|
||||
bch2_fs_quota_exit(c);
|
||||
bch2_fs_fsio_exit(c);
|
||||
|
@ -500,6 +506,7 @@ void __bch2_fs_stop(struct bch_fs *c)
|
|||
bch2_fs_debug_exit(c);
|
||||
bch2_fs_chardev_exit(c);
|
||||
|
||||
kobject_put(&c->counters_kobj);
|
||||
kobject_put(&c->time_stats);
|
||||
kobject_put(&c->opts_dir);
|
||||
kobject_put(&c->internal);
|
||||
|
@ -569,6 +576,7 @@ static int bch2_fs_online(struct bch_fs *c)
|
|||
kobject_add(&c->internal, &c->kobj, "internal") ?:
|
||||
kobject_add(&c->opts_dir, &c->kobj, "options") ?:
|
||||
kobject_add(&c->time_stats, &c->kobj, "time_stats") ?:
|
||||
kobject_add(&c->counters_kobj, &c->kobj, "counters") ?:
|
||||
bch2_opts_create_sysfs_files(&c->opts_dir);
|
||||
if (ret) {
|
||||
bch_err(c, "error creating sysfs objects");
|
||||
|
@ -617,6 +625,7 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts)
|
|||
kobject_init(&c->internal, &bch2_fs_internal_ktype);
|
||||
kobject_init(&c->opts_dir, &bch2_fs_opts_dir_ktype);
|
||||
kobject_init(&c->time_stats, &bch2_fs_time_stats_ktype);
|
||||
kobject_init(&c->counters_kobj, &bch2_fs_counters_ktype);
|
||||
|
||||
c->minor = -1;
|
||||
c->disk_sb.fs_sb = true;
|
||||
|
@ -777,7 +786,8 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts)
|
|||
bch2_fs_encryption_init(c) ?:
|
||||
bch2_fs_compress_init(c) ?:
|
||||
bch2_fs_ec_init(c) ?:
|
||||
bch2_fs_fsio_init(c);
|
||||
bch2_fs_fsio_init(c) ?:
|
||||
bch2_fs_counters_init(c);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
#include "util.h"
|
||||
|
||||
#define SYSFS_OPS(type) \
|
||||
struct sysfs_ops type ## _sysfs_ops = { \
|
||||
const struct sysfs_ops type ## _sysfs_ops = { \
|
||||
.show = type ## _show, \
|
||||
.store = type ## _store \
|
||||
}
|
||||
|
@ -195,6 +195,10 @@ read_attribute(extent_migrate_done);
|
|||
read_attribute(extent_migrate_raced);
|
||||
read_attribute(bucket_alloc_fail);
|
||||
|
||||
#define x(t, n, ...) read_attribute(t);
|
||||
BCH_PERSISTENT_COUNTERS()
|
||||
#undef x
|
||||
|
||||
rw_attribute(discard);
|
||||
rw_attribute(label);
|
||||
|
||||
|
@ -551,6 +555,47 @@ struct attribute *bch2_fs_files[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
/* counters dir */
|
||||
|
||||
SHOW(bch2_fs_counters)
|
||||
{
|
||||
struct bch_fs *c = container_of(kobj, struct bch_fs, counters_kobj);
|
||||
u64 counter = 0;
|
||||
u64 counter_since_mount = 0;
|
||||
|
||||
out->tabstops[0] = 32;
|
||||
#define x(t, ...) \
|
||||
if (attr == &sysfs_##t) { \
|
||||
counter = percpu_u64_get(&c->counters[BCH_COUNTER_##t]);\
|
||||
counter_since_mount = counter - c->counters_on_mount[BCH_COUNTER_##t];\
|
||||
pr_buf(out, "since mount:"); \
|
||||
pr_tab(out); \
|
||||
bch2_hprint(out, counter_since_mount << 9); \
|
||||
pr_newline(out); \
|
||||
\
|
||||
pr_buf(out, "since filesystem creation:"); \
|
||||
pr_tab(out); \
|
||||
bch2_hprint(out, counter << 9); \
|
||||
pr_newline(out); \
|
||||
}
|
||||
BCH_PERSISTENT_COUNTERS()
|
||||
#undef x
|
||||
return 0;
|
||||
}
|
||||
|
||||
STORE(bch2_fs_counters) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYSFS_OPS(bch2_fs_counters);
|
||||
|
||||
struct attribute *bch2_fs_counters_files[] = {
|
||||
#define x(t, ...) \
|
||||
&sysfs_##t,
|
||||
BCH_PERSISTENT_COUNTERS()
|
||||
#undef x
|
||||
NULL
|
||||
};
|
||||
/* internal dir - just a wrapper */
|
||||
|
||||
SHOW(bch2_fs_internal)
|
||||
|
|
|
@ -10,28 +10,32 @@ struct attribute;
|
|||
struct sysfs_ops;
|
||||
|
||||
extern struct attribute *bch2_fs_files[];
|
||||
extern struct attribute *bch2_fs_counters_files[];
|
||||
extern struct attribute *bch2_fs_internal_files[];
|
||||
extern struct attribute *bch2_fs_opts_dir_files[];
|
||||
extern struct attribute *bch2_fs_time_stats_files[];
|
||||
extern struct attribute *bch2_dev_files[];
|
||||
|
||||
extern struct sysfs_ops bch2_fs_sysfs_ops;
|
||||
extern struct sysfs_ops bch2_fs_internal_sysfs_ops;
|
||||
extern struct sysfs_ops bch2_fs_opts_dir_sysfs_ops;
|
||||
extern struct sysfs_ops bch2_fs_time_stats_sysfs_ops;
|
||||
extern struct sysfs_ops bch2_dev_sysfs_ops;
|
||||
extern const struct sysfs_ops bch2_fs_sysfs_ops;
|
||||
extern const struct sysfs_ops bch2_fs_counters_sysfs_ops;
|
||||
extern const struct sysfs_ops bch2_fs_internal_sysfs_ops;
|
||||
extern const struct sysfs_ops bch2_fs_opts_dir_sysfs_ops;
|
||||
extern const struct sysfs_ops bch2_fs_time_stats_sysfs_ops;
|
||||
extern const struct sysfs_ops bch2_dev_sysfs_ops;
|
||||
|
||||
int bch2_opts_create_sysfs_files(struct kobject *);
|
||||
|
||||
#else
|
||||
|
||||
static struct attribute *bch2_fs_files[] = {};
|
||||
static struct attribute *bch2_fs_counters_files[] = {};
|
||||
static struct attribute *bch2_fs_internal_files[] = {};
|
||||
static struct attribute *bch2_fs_opts_dir_files[] = {};
|
||||
static struct attribute *bch2_fs_time_stats_files[] = {};
|
||||
static struct attribute *bch2_dev_files[] = {};
|
||||
|
||||
static const struct sysfs_ops bch2_fs_sysfs_ops;
|
||||
static const struct sysfs_ops bch2_fs_counters_sysfs_ops;
|
||||
static const struct sysfs_ops bch2_fs_internal_sysfs_ops;
|
||||
static const struct sysfs_ops bch2_fs_opts_dir_sysfs_ops;
|
||||
static const struct sysfs_ops bch2_fs_time_stats_sysfs_ops;
|
||||
|
|
Loading…
Reference in New Issue