summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2017-12-22 20:37:30 -0500
committerKent Overstreet <kent.overstreet@gmail.com>2018-05-22 00:44:18 -0400
commite1d8a5857ab0bfe69ca772cf4f698032ad24ad04 (patch)
tree8d6cb0d8b5c29ca7ec32369d677e0e995abd8669
parent972b1a1113e09f48e01303a2fed688bb357682e6 (diff)
bcachefs: Add an ioctl for filesystem usage
-rw-r--r--fs/bcachefs/bcachefs_format.h3
-rw-r--r--fs/bcachefs/bcachefs_ioctl.h30
-rw-r--r--fs/bcachefs/buckets.c41
-rw-r--r--fs/bcachefs/buckets.h56
-rw-r--r--fs/bcachefs/chardev.c79
-rw-r--r--fs/bcachefs/fs.c4
-rw-r--r--fs/bcachefs/opts.c1
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
};