Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Daniel Hill and committed by
Kent Overstreet
104c6974 1f93726e

+223 -9
+1
fs/bcachefs/Makefile
··· 21 21 checksum.o \ 22 22 clock.o \ 23 23 compress.o \ 24 + counters.o \ 24 25 debug.o \ 25 26 dirent.o \ 26 27 disk_groups.o \
+5 -1
fs/bcachefs/bcachefs.h
··· 584 584 585 585 struct list_head list; 586 586 struct kobject kobj; 587 + struct kobject counters_kobj; 587 588 struct kobject internal; 588 589 struct kobject opts_dir; 589 590 struct kobject time_stats; ··· 901 900 902 901 u64 last_bucket_seq_cleanup; 903 902 904 - /* The rest of this all shows up in sysfs */ 903 + /* TODO rewrite as counters - The rest of this all shows up in sysfs */ 905 904 atomic_long_t read_realloc_races; 906 905 atomic_long_t extent_migrate_done; 907 906 atomic_long_t extent_migrate_raced; 908 907 atomic_long_t bucket_alloc_fail; 908 + 909 + u64 counters_on_mount[BCH_COUNTER_NR]; 910 + u64 __percpu *counters; 909 911 910 912 unsigned btree_gc_periodic:1; 911 913 unsigned copy_gc_enabled:1;
+21 -1
fs/bcachefs/bcachefs_format.h
··· 1090 1090 x(clean, 6) \ 1091 1091 x(replicas, 7) \ 1092 1092 x(journal_seq_blacklist, 8) \ 1093 - x(journal_v2, 9) 1093 + x(journal_v2, 9) \ 1094 + x(counters, 10) 1094 1095 1095 1096 enum bch_sb_field_type { 1096 1097 #define x(f, nr) BCH_SB_FIELD_##f = nr, ··· 1323 1322 struct bch_sb_field field; 1324 1323 struct bch_disk_group entries[0]; 1325 1324 } __attribute__((packed, aligned(8))); 1325 + 1326 + /* BCH_SB_FIELD_counters */ 1327 + 1328 + #define BCH_PERSISTENT_COUNTERS() \ 1329 + x(io_read, 0) \ 1330 + x(io_write, 1) \ 1331 + x(io_move, 2) 1332 + 1333 + enum bch_persistent_counters { 1334 + #define x(t, n, ...) BCH_COUNTER_##t, 1335 + BCH_PERSISTENT_COUNTERS() 1336 + #undef x 1337 + BCH_COUNTER_NR 1338 + }; 1339 + 1340 + struct bch_sb_field_counters { 1341 + struct bch_sb_field field; 1342 + __le64 d[0]; 1343 + }; 1326 1344 1327 1345 /* 1328 1346 * On clean shutdown, store btree roots and current journal sequence number in
+107
fs/bcachefs/counters.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include "bcachefs.h" 3 + #include "super-io.h" 4 + #include "counters.h" 5 + 6 + /* BCH_SB_FIELD_counters */ 7 + 8 + const char * const bch2_counter_names[] = { 9 + #define x(t, n, ...) (#t), 10 + BCH_PERSISTENT_COUNTERS() 11 + #undef x 12 + NULL 13 + }; 14 + 15 + static size_t bch2_sb_counter_nr_entries(struct bch_sb_field_counters *ctrs) 16 + { 17 + if (!ctrs) 18 + return 0; 19 + 20 + return (__le64 *) vstruct_end(&ctrs->field) - &ctrs->d[0]; 21 + }; 22 + 23 + static int bch2_sb_counters_validate(struct bch_sb *sb, 24 + struct bch_sb_field *f, 25 + struct printbuf *err) 26 + { 27 + return 0; 28 + }; 29 + 30 + void bch2_sb_counters_to_text(struct printbuf *out, struct bch_sb *sb, 31 + struct bch_sb_field *f) 32 + { 33 + struct bch_sb_field_counters *ctrs = field_to_type(f, counters); 34 + unsigned int i; 35 + unsigned int nr = bch2_sb_counter_nr_entries(ctrs); 36 + 37 + for (i = 0; i < nr; i++) { 38 + if (i < BCH_COUNTER_NR) 39 + pr_buf(out, "%s", bch2_counter_names[i]); 40 + else 41 + pr_buf(out, "(unknown)"); 42 + 43 + pr_tab(out); 44 + pr_buf(out, "%llu", le64_to_cpu(ctrs->d[i])); 45 + pr_newline(out); 46 + }; 47 + }; 48 + 49 + int bch2_sb_counters_to_cpu(struct bch_fs *c) 50 + { 51 + struct bch_sb_field_counters *ctrs = bch2_sb_get_counters(c->disk_sb.sb); 52 + unsigned int i; 53 + unsigned int nr = bch2_sb_counter_nr_entries(ctrs); 54 + u64 val = 0; 55 + 56 + for (i = 0; i < BCH_COUNTER_NR; i++) 57 + c->counters_on_mount[i] = 0; 58 + 59 + for (i = 0; i < min_t(unsigned int, nr, BCH_COUNTER_NR); i++) { 60 + val = le64_to_cpu(ctrs->d[i]); 61 + percpu_u64_set(&c->counters[i], val); 62 + c->counters_on_mount[i] = val; 63 + } 64 + return 0; 65 + }; 66 + 67 + int bch2_sb_counters_from_cpu(struct bch_fs *c) 68 + { 69 + struct bch_sb_field_counters *ctrs = bch2_sb_get_counters(c->disk_sb.sb); 70 + struct bch_sb_field_counters *ret; 71 + unsigned int i; 72 + unsigned int nr = bch2_sb_counter_nr_entries(ctrs); 73 + 74 + if (nr < BCH_COUNTER_NR) { 75 + ret = bch2_sb_resize_counters(&c->disk_sb, 76 + sizeof(*ctrs) / sizeof(u64) + BCH_COUNTER_NR); 77 + 78 + if (ret) { 79 + ctrs = ret; 80 + nr = bch2_sb_counter_nr_entries(ctrs); 81 + } 82 + } 83 + 84 + 85 + for (i = 0; i < min_t(unsigned int, nr, BCH_COUNTER_NR); i++) 86 + ctrs->d[i] = cpu_to_le64(percpu_u64_get(&c->counters[i])); 87 + return 0; 88 + } 89 + 90 + void bch2_fs_counters_exit(struct bch_fs *c) 91 + { 92 + free_percpu(c->counters); 93 + } 94 + 95 + int bch2_fs_counters_init(struct bch_fs *c) 96 + { 97 + c->counters = __alloc_percpu(sizeof(u64) * BCH_COUNTER_NR, sizeof(u64)); 98 + if (!c->counters) 99 + return -ENOMEM; 100 + 101 + return bch2_sb_counters_to_cpu(c); 102 + } 103 + 104 + const struct bch_sb_field_ops bch_sb_field_ops_counters = { 105 + .validate = bch2_sb_counters_validate, 106 + .to_text = bch2_sb_counters_to_text, 107 + };
+17
fs/bcachefs/counters.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef _BCACHEFS_COUNTERS_H 3 + #define _BCACHEFS_COUNTERS_H 4 + 5 + #include "bcachefs.h" 6 + #include "super-io.h" 7 + 8 + 9 + int bch2_sb_counters_to_cpu(struct bch_fs *); 10 + int bch2_sb_counters_from_cpu(struct bch_fs *); 11 + 12 + void bch2_fs_counters_exit(struct bch_fs *); 13 + int bch2_fs_counters_init(struct bch_fs *); 14 + 15 + extern const struct bch_sb_field_ops bch_sb_field_ops_counters; 16 + 17 + #endif // _BCACHEFS_COUNTERS_H
+2
fs/bcachefs/io.c
··· 1403 1403 goto err; 1404 1404 } 1405 1405 1406 + this_cpu_add(c->counters[BCH_COUNTER_io_write], bio_sectors(bio)); 1406 1407 bch2_increment_clock(c, bio_sectors(bio), WRITE); 1407 1408 1408 1409 data_len = min_t(u64, bio->bi_iter.bi_size, ··· 2311 2310 if (rbio->bounce) 2312 2311 trace_read_bounce(&rbio->bio); 2313 2312 2313 + this_cpu_add(c->counters[BCH_COUNTER_io_read], bio_sectors(&rbio->bio)); 2314 2314 bch2_increment_clock(c, bio_sectors(&rbio->bio), READ); 2315 2315 2316 2316 /*
+1
fs/bcachefs/move.c
··· 575 575 576 576 atomic64_inc(&ctxt->stats->keys_moved); 577 577 atomic64_add(k.k->size, &ctxt->stats->sectors_moved); 578 + this_cpu_add(c->counters[BCH_COUNTER_io_move], k.k->size); 578 579 579 580 trace_move_extent(k.k); 580 581
+3
fs/bcachefs/super-io.c
··· 18 18 #include "super.h" 19 19 #include "trace.h" 20 20 #include "vstructs.h" 21 + #include "counters.h" 21 22 22 23 #include <linux/backing-dev.h> 23 24 #include <linux/sort.h> ··· 819 818 SET_BCH_SB_HAS_TOPOLOGY_ERRORS(c->disk_sb.sb, 1); 820 819 821 820 SET_BCH_SB_BIG_ENDIAN(c->disk_sb.sb, CPU_BIG_ENDIAN); 821 + 822 + bch2_sb_counters_from_cpu(c); 822 823 823 824 for_each_online_member(ca, c, i) 824 825 bch2_sb_from_fs(c, ca);
+11 -1
fs/bcachefs/super.c
··· 21 21 #include "checksum.h" 22 22 #include "clock.h" 23 23 #include "compress.h" 24 + #include "counters.h" 24 25 #include "debug.h" 25 26 #include "disk_groups.h" 26 27 #include "ec.h" ··· 79 78 80 79 static void bch2_fs_release(struct kobject *); 81 80 static void bch2_dev_release(struct kobject *); 81 + static void bch2_fs_counters_release(struct kobject *k) 82 + { 83 + } 82 84 83 85 static void bch2_fs_internal_release(struct kobject *k) 84 86 { ··· 96 92 } 97 93 98 94 KTYPE(bch2_fs); 95 + KTYPE(bch2_fs_counters); 99 96 KTYPE(bch2_fs_internal); 100 97 KTYPE(bch2_fs_opts_dir); 101 98 KTYPE(bch2_fs_time_stats); ··· 421 416 for (i = 0; i < BCH_TIME_STAT_NR; i++) 422 417 bch2_time_stats_exit(&c->times[i]); 423 418 419 + bch2_fs_counters_exit(c); 424 420 bch2_fs_snapshots_exit(c); 425 421 bch2_fs_quota_exit(c); 426 422 bch2_fs_fsio_exit(c); ··· 506 500 bch2_fs_debug_exit(c); 507 501 bch2_fs_chardev_exit(c); 508 502 503 + kobject_put(&c->counters_kobj); 509 504 kobject_put(&c->time_stats); 510 505 kobject_put(&c->opts_dir); 511 506 kobject_put(&c->internal); ··· 576 569 kobject_add(&c->internal, &c->kobj, "internal") ?: 577 570 kobject_add(&c->opts_dir, &c->kobj, "options") ?: 578 571 kobject_add(&c->time_stats, &c->kobj, "time_stats") ?: 572 + kobject_add(&c->counters_kobj, &c->kobj, "counters") ?: 579 573 bch2_opts_create_sysfs_files(&c->opts_dir); 580 574 if (ret) { 581 575 bch_err(c, "error creating sysfs objects"); ··· 625 617 kobject_init(&c->internal, &bch2_fs_internal_ktype); 626 618 kobject_init(&c->opts_dir, &bch2_fs_opts_dir_ktype); 627 619 kobject_init(&c->time_stats, &bch2_fs_time_stats_ktype); 620 + kobject_init(&c->counters_kobj, &bch2_fs_counters_ktype); 628 621 629 622 c->minor = -1; 630 623 c->disk_sb.fs_sb = true; ··· 786 777 bch2_fs_encryption_init(c) ?: 787 778 bch2_fs_compress_init(c) ?: 788 779 bch2_fs_ec_init(c) ?: 789 - bch2_fs_fsio_init(c); 780 + bch2_fs_fsio_init(c) ?: 781 + bch2_fs_counters_init(c); 790 782 if (ret) 791 783 goto err; 792 784
+46 -1
fs/bcachefs/sysfs.c
··· 40 40 #include "util.h" 41 41 42 42 #define SYSFS_OPS(type) \ 43 - struct sysfs_ops type ## _sysfs_ops = { \ 43 + const struct sysfs_ops type ## _sysfs_ops = { \ 44 44 .show = type ## _show, \ 45 45 .store = type ## _store \ 46 46 } ··· 194 194 read_attribute(extent_migrate_done); 195 195 read_attribute(extent_migrate_raced); 196 196 read_attribute(bucket_alloc_fail); 197 + 198 + #define x(t, n, ...) read_attribute(t); 199 + BCH_PERSISTENT_COUNTERS() 200 + #undef x 197 201 198 202 rw_attribute(discard); 199 203 rw_attribute(label); ··· 555 551 NULL 556 552 }; 557 553 554 + /* counters dir */ 555 + 556 + SHOW(bch2_fs_counters) 557 + { 558 + struct bch_fs *c = container_of(kobj, struct bch_fs, counters_kobj); 559 + u64 counter = 0; 560 + u64 counter_since_mount = 0; 561 + 562 + out->tabstops[0] = 32; 563 + #define x(t, ...) \ 564 + if (attr == &sysfs_##t) { \ 565 + counter = percpu_u64_get(&c->counters[BCH_COUNTER_##t]);\ 566 + counter_since_mount = counter - c->counters_on_mount[BCH_COUNTER_##t];\ 567 + pr_buf(out, "since mount:"); \ 568 + pr_tab(out); \ 569 + bch2_hprint(out, counter_since_mount << 9); \ 570 + pr_newline(out); \ 571 + \ 572 + pr_buf(out, "since filesystem creation:"); \ 573 + pr_tab(out); \ 574 + bch2_hprint(out, counter << 9); \ 575 + pr_newline(out); \ 576 + } 577 + BCH_PERSISTENT_COUNTERS() 578 + #undef x 579 + return 0; 580 + } 581 + 582 + STORE(bch2_fs_counters) { 583 + return 0; 584 + } 585 + 586 + SYSFS_OPS(bch2_fs_counters); 587 + 588 + struct attribute *bch2_fs_counters_files[] = { 589 + #define x(t, ...) \ 590 + &sysfs_##t, 591 + BCH_PERSISTENT_COUNTERS() 592 + #undef x 593 + NULL 594 + }; 558 595 /* internal dir - just a wrapper */ 559 596 560 597 SHOW(bch2_fs_internal)
+9 -5
fs/bcachefs/sysfs.h
··· 10 10 struct sysfs_ops; 11 11 12 12 extern struct attribute *bch2_fs_files[]; 13 + extern struct attribute *bch2_fs_counters_files[]; 13 14 extern struct attribute *bch2_fs_internal_files[]; 14 15 extern struct attribute *bch2_fs_opts_dir_files[]; 15 16 extern struct attribute *bch2_fs_time_stats_files[]; 16 17 extern struct attribute *bch2_dev_files[]; 17 18 18 - extern struct sysfs_ops bch2_fs_sysfs_ops; 19 - extern struct sysfs_ops bch2_fs_internal_sysfs_ops; 20 - extern struct sysfs_ops bch2_fs_opts_dir_sysfs_ops; 21 - extern struct sysfs_ops bch2_fs_time_stats_sysfs_ops; 22 - extern struct sysfs_ops bch2_dev_sysfs_ops; 19 + extern const struct sysfs_ops bch2_fs_sysfs_ops; 20 + extern const struct sysfs_ops bch2_fs_counters_sysfs_ops; 21 + extern const struct sysfs_ops bch2_fs_internal_sysfs_ops; 22 + extern const struct sysfs_ops bch2_fs_opts_dir_sysfs_ops; 23 + extern const struct sysfs_ops bch2_fs_time_stats_sysfs_ops; 24 + extern const struct sysfs_ops bch2_dev_sysfs_ops; 23 25 24 26 int bch2_opts_create_sysfs_files(struct kobject *); 25 27 26 28 #else 27 29 28 30 static struct attribute *bch2_fs_files[] = {}; 31 + static struct attribute *bch2_fs_counters_files[] = {}; 29 32 static struct attribute *bch2_fs_internal_files[] = {}; 30 33 static struct attribute *bch2_fs_opts_dir_files[] = {}; 31 34 static struct attribute *bch2_fs_time_stats_files[] = {}; 32 35 static struct attribute *bch2_dev_files[] = {}; 33 36 34 37 static const struct sysfs_ops bch2_fs_sysfs_ops; 38 + static const struct sysfs_ops bch2_fs_counters_sysfs_ops; 35 39 static const struct sysfs_ops bch2_fs_internal_sysfs_ops; 36 40 static const struct sysfs_ops bch2_fs_opts_dir_sysfs_ops; 37 41 static const struct sysfs_ops bch2_fs_time_stats_sysfs_ops;