diff options
author | Kent Overstreet <kent.overstreet@gmail.com> | 2017-12-22 20:37:30 -0500 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@gmail.com> | 2018-05-22 00:44:18 -0400 |
commit | e1d8a5857ab0bfe69ca772cf4f698032ad24ad04 (patch) | |
tree | 8d6cb0d8b5c29ca7ec32369d677e0e995abd8669 | |
parent | 972b1a1113e09f48e01303a2fed688bb357682e6 (diff) |
bcachefs: Add an ioctl for filesystem usage
-rw-r--r-- | fs/bcachefs/bcachefs_format.h | 3 | ||||
-rw-r--r-- | fs/bcachefs/bcachefs_ioctl.h | 30 | ||||
-rw-r--r-- | fs/bcachefs/buckets.c | 41 | ||||
-rw-r--r-- | fs/bcachefs/buckets.h | 56 | ||||
-rw-r--r-- | fs/bcachefs/chardev.c | 79 | ||||
-rw-r--r-- | fs/bcachefs/fs.c | 4 | ||||
-rw-r--r-- | fs/bcachefs/opts.c | 1 |
7 files changed, 171 insertions, 43 deletions
diff --git a/fs/bcachefs/bcachefs_format.h b/fs/bcachefs/bcachefs_format.h index 6e0e0452bbb5..0c5af084477b 100644 --- a/fs/bcachefs/bcachefs_format.h +++ b/fs/bcachefs/bcachefs_format.h @@ -883,7 +883,8 @@ enum bch_data_type { BCH_DATA_JOURNAL = 2, BCH_DATA_BTREE = 3, BCH_DATA_USER = 4, - BCH_DATA_NR = 5, + BCH_DATA_CACHED = 5, + BCH_DATA_NR = 6, }; struct bch_replicas_entry { diff --git a/fs/bcachefs/bcachefs_ioctl.h b/fs/bcachefs/bcachefs_ioctl.h index 5bdbbe6ef1ef..079e2b556afd 100644 --- a/fs/bcachefs/bcachefs_ioctl.h +++ b/fs/bcachefs/bcachefs_ioctl.h @@ -45,6 +45,7 @@ struct bch_ioctl_incremental { #define BCH_IOCTL_DISK_SET_STATE _IOW(0xbc, 8, struct bch_ioctl_disk_set_state) #define BCH_IOCTL_DISK_EVACUATE _IOW(0xbc, 9, struct bch_ioctl_disk) #define BCH_IOCTL_DATA _IOW(0xbc, 10, struct bch_ioctl_data) +#define BCH_IOCTL_USAGE _IOWR(0xbc, 11, struct bch_ioctl_usage) struct bch_ioctl_query_uuid { uuid_le uuid; @@ -93,4 +94,33 @@ struct bch_ioctl_data { __u64 end_offset; }; +struct bch_ioctl_dev_usage { + __u8 state; + __u8 alive; + __u8 pad[6]; + __u32 dev; + + __u32 bucket_size; + __u64 nr_buckets; + + __u64 buckets[BCH_DATA_NR]; + __u64 sectors[BCH_DATA_NR]; +}; + +struct bch_ioctl_fs_usage { + __u64 capacity; + __u64 used; + __u64 online_reserved; + __u64 persistent_reserved[BCH_REPLICAS_MAX]; + __u64 sectors[BCH_DATA_NR][BCH_REPLICAS_MAX]; +}; + +struct bch_ioctl_usage { + __u16 nr_devices; + __u16 pad[3]; + + struct bch_ioctl_fs_usage fs; + struct bch_ioctl_dev_usage devs[0]; +}; + #endif /* _BCACHEFS_IOCTL_H */ diff --git a/fs/bcachefs/buckets.c b/fs/bcachefs/buckets.c index 555fd969d1de..849668815f17 100644 --- a/fs/bcachefs/buckets.c +++ b/fs/bcachefs/buckets.c @@ -224,6 +224,45 @@ bch2_fs_usage_read(struct bch_fs *c) c->usage_percpu); } +struct fs_usage_sum { + u64 data; + u64 reserved; +}; + +static inline struct fs_usage_sum __fs_usage_sum(struct bch_fs_usage stats) +{ + struct fs_usage_sum sum = { 0 }; + unsigned i; + + for (i = 0; i < BCH_REPLICAS_MAX; i++) { + sum.data += (stats.s[i].data[S_META] + + stats.s[i].data[S_DIRTY]) * (i + 1); + sum.reserved += stats.s[i].persistent_reserved * (i + 1); + } + + sum.reserved += stats.online_reserved; + return sum; +} + +#define RESERVE_FACTOR 6 + +static u64 reserve_factor(u64 r) +{ + return r + (round_up(r, (1 << RESERVE_FACTOR)) >> RESERVE_FACTOR); +} + +u64 __bch2_fs_sectors_used(struct bch_fs *c, struct bch_fs_usage stats) +{ + struct fs_usage_sum sum = __fs_usage_sum(stats); + + return sum.data + reserve_factor(sum.reserved); +} + +u64 bch2_fs_sectors_used(struct bch_fs *c, struct bch_fs_usage stats) +{ + return min(c->capacity, __bch2_fs_sectors_used(c, stats)); +} + static inline int is_meta_bucket(struct bucket_mark m) { return m.data_type != BUCKET_DATA; @@ -675,7 +714,7 @@ static u64 __recalc_sectors_available(struct bch_fs *c) for_each_possible_cpu(cpu) per_cpu_ptr(c->usage_percpu, cpu)->available_cache = 0; - avail = c->capacity - bch2_fs_sectors_used(c); + avail = c->capacity - bch2_fs_sectors_used(c, bch2_fs_usage_read(c)); avail <<= RESERVE_FACTOR; avail /= (1 << RESERVE_FACTOR) + 1; diff --git a/fs/bcachefs/buckets.h b/fs/bcachefs/buckets.h index 7d2b08cbc274..6b152da91e6a 100644 --- a/fs/bcachefs/buckets.h +++ b/fs/bcachefs/buckets.h @@ -92,7 +92,7 @@ static inline bool bucket_unused(struct bucket_mark mark) !bucket_sectors_used(mark); } -/* Per device stats: */ +/* Device usage: */ struct bch_dev_usage __bch2_dev_usage_read(struct bch_dev *); struct bch_dev_usage bch2_dev_usage_read(struct bch_fs *, struct bch_dev *); @@ -130,51 +130,27 @@ static inline u64 dev_buckets_free(struct bch_fs *c, struct bch_dev *ca) return __dev_buckets_free(ca, bch2_dev_usage_read(c, ca)); } -/* Cache set stats: */ +/* Filesystem usage: */ -struct bch_fs_usage __bch2_fs_usage_read(struct bch_fs *); -struct bch_fs_usage bch2_fs_usage_read(struct bch_fs *); -void bch2_fs_usage_apply(struct bch_fs *, struct bch_fs_usage *, - struct disk_reservation *, struct gc_pos); - -struct fs_usage_sum { - u64 data; - u64 reserved; -}; - -static inline struct fs_usage_sum __fs_usage_sum(struct bch_fs_usage stats) +static inline enum bch_data_type s_alloc_to_data_type(enum s_alloc s) { - struct fs_usage_sum sum = { 0 }; - unsigned i; - - for (i = 0; i < BCH_REPLICAS_MAX; i++) { - sum.data += (stats.s[i].data[S_META] + - stats.s[i].data[S_DIRTY]) * (i + 1); - sum.reserved += stats.s[i].persistent_reserved * (i + 1); + switch (s) { + case S_META: + return BCH_DATA_BTREE; + case S_DIRTY: + return BCH_DATA_USER; + default: + BUG(); } - - sum.reserved += stats.online_reserved; - return sum; } -#define RESERVE_FACTOR 6 - -static u64 reserve_factor(u64 r) -{ - return r + (round_up(r, (1 << RESERVE_FACTOR)) >> RESERVE_FACTOR); -} - -static inline u64 __bch2_fs_sectors_used(struct bch_fs *c) -{ - struct fs_usage_sum sum = __fs_usage_sum(__bch2_fs_usage_read(c)); - - return sum.data + reserve_factor(sum.reserved); -} +struct bch_fs_usage __bch2_fs_usage_read(struct bch_fs *); +struct bch_fs_usage bch2_fs_usage_read(struct bch_fs *); +void bch2_fs_usage_apply(struct bch_fs *, struct bch_fs_usage *, + struct disk_reservation *, struct gc_pos); -static inline u64 bch2_fs_sectors_used(struct bch_fs *c) -{ - return min(c->capacity, __bch2_fs_sectors_used(c)); -} +u64 __bch2_fs_sectors_used(struct bch_fs *, struct bch_fs_usage); +u64 bch2_fs_sectors_used(struct bch_fs *, struct bch_fs_usage); static inline bool is_available_bucket(struct bucket_mark mark) { diff --git a/fs/bcachefs/chardev.c b/fs/bcachefs/chardev.c index 24af2ca1620e..4c818bb3cd09 100644 --- a/fs/bcachefs/chardev.c +++ b/fs/bcachefs/chardev.c @@ -2,6 +2,7 @@ #include "bcachefs.h" #include "bcachefs_ioctl.h" +#include "buckets.h" #include "chardev.h" #include "super.h" #include "super-io.h" @@ -289,6 +290,82 @@ static long bch2_ioctl_disk_evacuate(struct bch_fs *c, return ret; } +static long bch2_ioctl_usage(struct bch_fs *c, + struct bch_ioctl_usage __user *user_arg) +{ + struct bch_ioctl_usage arg; + struct bch_dev *ca; + unsigned i, j; + int ret; + + if (copy_from_user(&arg, user_arg, sizeof(arg))) + return -EFAULT; + + for (i = 0; i < arg.nr_devices; i++) { + struct bch_ioctl_dev_usage dst = { .alive = 0 }; + + ret = copy_to_user(&user_arg->devs[i], &dst, sizeof(dst)); + if (ret) + return ret; + } + + { + struct bch_fs_usage src = bch2_fs_usage_read(c); + struct bch_ioctl_fs_usage dst = { + .capacity = c->capacity, + .used = bch2_fs_sectors_used(c, src), + .online_reserved = src.online_reserved, + }; + + for (i = 0; i < BCH_REPLICAS_MAX; i++) { + dst.persistent_reserved[i] = + src.s[i].persistent_reserved; + + for (j = 0; j < S_ALLOC_NR; j++) + dst.sectors[s_alloc_to_data_type(j)][i] = + src.s[i].data[j]; + } + + ret = copy_to_user(&user_arg->fs, &dst, sizeof(dst)); + if (ret) + return ret; + } + + for_each_member_device(ca, c, i) { + struct bch_dev_usage src = bch2_dev_usage_read(c, ca); + struct bch_ioctl_dev_usage dst = { + .alive = 1, + .state = ca->mi.state, + .bucket_size = ca->mi.bucket_size, + .nr_buckets = ca->mi.nbuckets - ca->mi.first_bucket, + }; + + if (ca->dev_idx >= arg.nr_devices) { + percpu_ref_put(&ca->ref); + return -ENOSPC; + } + + if (percpu_ref_tryget(&ca->io_ref)) { + dst.dev = huge_encode_dev(ca->disk_sb.bdev->bd_dev); + percpu_ref_put(&ca->io_ref); + } + + for (j = 0; j < S_ALLOC_NR; j++) { + dst.buckets[s_alloc_to_data_type(j)] = src.buckets[j]; + dst.sectors[s_alloc_to_data_type(j)] = src.sectors[j]; + } + + dst.buckets[BCH_DATA_CACHED] = src.buckets_cached; + dst.sectors[BCH_DATA_CACHED] = src.sectors_cached; + + ret = copy_to_user(&user_arg->devs[i], &dst, sizeof(dst)); + if (ret) + return ret; + } + + return 0; +} + #define BCH_IOCTL(_name, _argtype) \ do { \ _argtype i; \ @@ -304,6 +381,8 @@ long bch2_fs_ioctl(struct bch_fs *c, unsigned cmd, void __user *arg) switch (cmd) { case BCH_IOCTL_QUERY_UUID: return bch2_ioctl_query_uuid(c, arg); + case BCH_IOCTL_USAGE: + return bch2_ioctl_usage(c, arg); } if (!capable(CAP_SYS_ADMIN)) diff --git a/fs/bcachefs/fs.c b/fs/bcachefs/fs.c index cb0397f1343b..472df23a4ad1 100644 --- a/fs/bcachefs/fs.c +++ b/fs/bcachefs/fs.c @@ -1009,7 +1009,9 @@ static int bch2_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_type = BCACHEFS_STATFS_MAGIC; buf->f_bsize = sb->s_blocksize; buf->f_blocks = c->capacity >> PAGE_SECTOR_SHIFT; - buf->f_bfree = (c->capacity - bch2_fs_sectors_used(c)) >> PAGE_SECTOR_SHIFT; + buf->f_bfree = (c->capacity - + bch2_fs_sectors_used(c, bch2_fs_usage_read(c))) >> + PAGE_SECTOR_SHIFT; buf->f_bavail = buf->f_bfree; buf->f_files = atomic_long_read(&c->nr_inodes); buf->f_ffree = U64_MAX; diff --git a/fs/bcachefs/opts.c b/fs/bcachefs/opts.c index 28e40e41b08e..e6833d95f0ed 100644 --- a/fs/bcachefs/opts.c +++ b/fs/bcachefs/opts.c @@ -38,6 +38,7 @@ const char * const bch2_data_types[] = { "journal", "btree", "data", + "cached", NULL }; |