summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/bcachefs/bcachefs_format.h3
-rw-r--r--fs/bcachefs/bcachefs_ioctl.h70
-rw-r--r--fs/bcachefs/chardev.c152
-rw-r--r--fs/bcachefs/replicas.h3
4 files changed, 136 insertions, 92 deletions
diff --git a/fs/bcachefs/bcachefs_format.h b/fs/bcachefs/bcachefs_format.h
index e3004593874c..9b8fc265a5c0 100644
--- a/fs/bcachefs/bcachefs_format.h
+++ b/fs/bcachefs/bcachefs_format.h
@@ -1084,6 +1084,9 @@ struct bch_replicas_entry {
__u8 devs[];
} __attribute__((packed));
+#define replicas_entry_bytes(_i) \
+ (offsetof(typeof(*(_i)), devs) + (_i)->nr_devs)
+
struct bch_sb_field_replicas {
struct bch_sb_field field;
struct bch_replicas_entry entries[];
diff --git a/fs/bcachefs/bcachefs_ioctl.h b/fs/bcachefs/bcachefs_ioctl.h
index 8c0599618404..d7f25e52dc71 100644
--- a/fs/bcachefs/bcachefs_ioctl.h
+++ b/fs/bcachefs/bcachefs_ioctl.h
@@ -68,7 +68,8 @@ struct bch_ioctl_incremental {
#define BCH_IOCTL_DISK_OFFLINE _IOW(0xbc, 7, struct bch_ioctl_disk)
#define BCH_IOCTL_DISK_SET_STATE _IOW(0xbc, 8, struct bch_ioctl_disk_set_state)
#define BCH_IOCTL_DATA _IOW(0xbc, 10, struct bch_ioctl_data)
-#define BCH_IOCTL_USAGE _IOWR(0xbc, 11, struct bch_ioctl_usage)
+#define BCH_IOCTL_FS_USAGE _IOWR(0xbc, 11, struct bch_ioctl_fs_usage)
+#define BCH_IOCTL_DEV_USAGE _IOWR(0xbc, 11, struct bch_ioctl_dev_usage)
#define BCH_IOCTL_READ_SUPER _IOW(0xbc, 12, struct bch_ioctl_read_super)
#define BCH_IOCTL_DISK_GET_IDX _IOW(0xbc, 13, struct bch_ioctl_disk_get_idx)
#define BCH_IOCTL_DISK_RESIZE _IOW(0xbc, 14, struct bch_ioctl_disk_resize)
@@ -224,46 +225,59 @@ struct bch_ioctl_data_event {
};
} __attribute__((packed, aligned(8)));
-struct bch_ioctl_dev_usage {
- __u8 state;
- __u8 alive;
- __u8 pad[6];
- __u32 dev;
+struct bch_replicas_usage {
+ __u64 sectors;
+ struct bch_replicas_entry r;
+} __attribute__((packed));
- __u32 bucket_size;
- __u64 nr_buckets;
-
- __u64 buckets[BCH_DATA_NR];
- __u64 sectors[BCH_DATA_NR];
-};
+static inline struct bch_replicas_usage *
+replicas_usage_next(struct bch_replicas_usage *u)
+{
+ return (void *) u + replicas_entry_bytes(&u->r) + 8;
+}
+/*
+ * BCH_IOCTL_FS_USAGE: query filesystem disk space usage
+ *
+ * Returns disk space usage broken out by data type, number of replicas, and
+ * by component device
+ *
+ * @replica_entries_bytes - size, in bytes, allocated for replica usage entries
+ *
+ * On success, @replica_entries_bytes will be changed to indicate the number of
+ * bytes actually used.
+ *
+ * Returns -ERANGE if @replica_entries_bytes was too small
+ */
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];
+
+ __u32 replica_entries_bytes;
+ __u32 pad;
+
+ struct bch_replicas_usage replicas[0];
};
/*
- * BCH_IOCTL_USAGE: query filesystem disk space usage
- *
- * Returns disk space usage broken out by data type, number of replicas, and
- * by component device
+ * BCH_IOCTL_DEV_USAGE: query device disk space usage
*
- * @nr_devices - number of devices userspace allocated space for in @devs
- *
- * On success, @fs and @devs will be filled out appropriately and devs[i].alive
- * will indicate if a device was present in that slot
- *
- * Returns -ERANGE if @nr_devices was too small
+ * Returns disk space usage broken out by data type - both by buckets and
+ * sectors.
*/
-struct bch_ioctl_usage {
- __u16 nr_devices;
- __u16 pad[3];
+struct bch_ioctl_dev_usage {
+ __u64 dev;
+ __u32 flags;
+ __u8 state;
+ __u8 pad[7];
+
+ __u32 bucket_size;
+ __u64 nr_buckets;
- struct bch_ioctl_fs_usage fs;
- struct bch_ioctl_dev_usage devs[0];
+ __u64 buckets[BCH_DATA_NR];
+ __u64 sectors[BCH_DATA_NR];
};
/*
diff --git a/fs/bcachefs/chardev.c b/fs/bcachefs/chardev.c
index 4d8331022648..084bef5e7997 100644
--- a/fs/bcachefs/chardev.c
+++ b/fs/bcachefs/chardev.c
@@ -6,6 +6,7 @@
#include "buckets.h"
#include "chardev.h"
#include "move.h"
+#include "replicas.h"
#include "super.h"
#include "super-io.h"
@@ -371,89 +372,116 @@ err:
return ret;
}
-static long bch2_ioctl_usage(struct bch_fs *c,
- struct bch_ioctl_usage __user *user_arg)
+static long bch2_ioctl_fs_usage(struct bch_fs *c,
+ struct bch_ioctl_fs_usage __user *user_arg)
{
- struct bch_ioctl_usage arg;
- struct bch_dev *ca;
- unsigned i, j;
- int ret;
+ struct bch_ioctl_fs_usage *arg = NULL;
+ struct bch_replicas_usage *dst_e, *dst_end;
+ struct bch_fs_usage_online *src;
+ u32 replica_entries_bytes;
+ unsigned i;
+ int ret = 0;
if (!test_bit(BCH_FS_STARTED, &c->flags))
return -EINVAL;
- if (copy_from_user(&arg, user_arg, sizeof(arg)))
+ if (get_user(replica_entries_bytes, &user_arg->replica_entries_bytes))
return -EFAULT;
- for (i = 0; i < arg.nr_devices; i++) {
- struct bch_ioctl_dev_usage dst = { .alive = 0 };
+ arg = kzalloc(sizeof(*arg) + replica_entries_bytes, GFP_KERNEL);
+ if (!arg)
+ return -ENOMEM;
- ret = copy_to_user(&user_arg->devs[i], &dst, sizeof(dst));
- if (ret)
- return ret;
+ src = bch2_fs_usage_read(c);
+ if (!src) {
+ ret = -ENOMEM;
+ goto err;
}
- {
- struct bch_fs_usage_online *src;
- struct bch_ioctl_fs_usage dst = {
- .capacity = c->capacity,
- };
+ arg->capacity = c->capacity;
+ arg->used = bch2_fs_sectors_used(c, src);
+ arg->online_reserved = src->online_reserved;
- src = bch2_fs_usage_read(c);
- if (!src)
- return -ENOMEM;
+ for (i = 0; i < BCH_REPLICAS_MAX; i++)
+ arg->persistent_reserved[i] = src->u.persistent_reserved[i];
- dst.used = bch2_fs_sectors_used(c, src);
- dst.online_reserved = src->online_reserved;
+ dst_e = arg->replicas;
+ dst_end = (void *) arg->replicas + replica_entries_bytes;
- percpu_up_read(&c->mark_lock);
+ for (i = 0; i < c->replicas.nr; i++) {
+ struct bch_replicas_entry *src_e =
+ cpu_replicas_entry(&c->replicas, i);
- for (i = 0; i < BCH_REPLICAS_MAX; i++) {
- dst.persistent_reserved[i] =
- src->u.persistent_reserved[i];
-#if 0
- for (j = 0; j < BCH_DATA_NR; j++)
- dst.sectors[j][i] = src.replicas[i].data[j];
-#endif
+ if (replicas_usage_next(dst_e) > dst_end) {
+ ret = -ERANGE;
+ break;
}
- kfree(src);
+ dst_e->sectors = src->u.replicas[i];
+ dst_e->r = *src_e;
+
+ /* recheck after setting nr_devs: */
+ if (replicas_usage_next(dst_e) > dst_end) {
+ ret = -ERANGE;
+ break;
+ }
- ret = copy_to_user(&user_arg->fs, &dst, sizeof(dst));
- if (ret)
- return ret;
+ memcpy(dst_e->r.devs, src_e->devs, src_e->nr_devs);
+
+ dst_e = replicas_usage_next(dst_e);
}
- 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 -ERANGE;
- }
+ arg->replica_entries_bytes = (void *) dst_e - (void *) arg->replicas;
- if (percpu_ref_tryget(&ca->io_ref)) {
- dst.dev = huge_encode_dev(ca->disk_sb.bdev->bd_dev);
- percpu_ref_put(&ca->io_ref);
- }
+ percpu_up_read(&c->mark_lock);
+ kfree(src);
- for (j = 0; j < BCH_DATA_NR; j++) {
- dst.buckets[j] = src.buckets[j];
- dst.sectors[j] = src.sectors[j];
- }
+ if (!ret)
+ ret = copy_to_user(user_arg, arg,
+ sizeof(*arg) + arg->replica_entries_bytes);
+err:
+ kfree(arg);
+ return ret;
+}
+
+static long bch2_ioctl_dev_usage(struct bch_fs *c,
+ struct bch_ioctl_dev_usage __user *user_arg)
+{
+ struct bch_ioctl_dev_usage arg;
+ struct bch_dev_usage src;
+ struct bch_dev *ca;
+ unsigned i;
+
+ if (!test_bit(BCH_FS_STARTED, &c->flags))
+ return -EINVAL;
- ret = copy_to_user(&user_arg->devs[i], &dst, sizeof(dst));
- if (ret)
- return ret;
+ if (copy_from_user(&arg, user_arg, sizeof(arg)))
+ return -EFAULT;
+
+ if ((arg.flags & ~BCH_BY_INDEX) ||
+ arg.pad[0] ||
+ arg.pad[1] ||
+ arg.pad[2])
+ return -EINVAL;
+
+ ca = bch2_device_lookup(c, arg.dev, arg.flags);
+ if (IS_ERR(ca))
+ return PTR_ERR(ca);
+
+ src = bch2_dev_usage_read(c, ca);
+
+ arg.state = ca->mi.state;
+ arg.bucket_size = ca->mi.bucket_size;
+ arg.nr_buckets = ca->mi.nbuckets - ca->mi.first_bucket;
+
+ for (i = 0; i < BCH_DATA_NR; i++) {
+ arg.buckets[i] = src.buckets[i];
+ arg.sectors[i] = src.sectors[i];
}
- return 0;
+ percpu_ref_put(&ca->ref);
+
+ return copy_to_user(user_arg, &arg, sizeof(arg));
}
static long bch2_ioctl_read_super(struct bch_fs *c,
@@ -547,8 +575,10 @@ 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);
+ case BCH_IOCTL_FS_USAGE:
+ return bch2_ioctl_fs_usage(c, arg);
+ case BCH_IOCTL_DEV_USAGE:
+ return bch2_ioctl_dev_usage(c, arg);
}
if (!capable(CAP_SYS_ADMIN))
diff --git a/fs/bcachefs/replicas.h b/fs/bcachefs/replicas.h
index 0d6e19126021..8527d82841bb 100644
--- a/fs/bcachefs/replicas.h
+++ b/fs/bcachefs/replicas.h
@@ -72,9 +72,6 @@ int bch2_replicas_set_usage(struct bch_fs *,
/* iterate over superblock replicas - used by userspace tools: */
-#define replicas_entry_bytes(_i) \
- (offsetof(typeof(*(_i)), devs) + (_i)->nr_devs)
-
#define replicas_entry_next(_i) \
((typeof(_i)) ((void *) (_i) + replicas_entry_bytes(_i)))