]> git.sesse.net Git - bcachefs-tools-debian/blobdiff - libbcachefs/chardev.c
Update bcachefs sources to 9abf628c70 bcachefs: Fix a spurious error in fsck
[bcachefs-tools-debian] / libbcachefs / chardev.c
index 1ab36ac9f5198fc08cd0136662acead738804e60..5593b9a1de27cc7a737cb46a88fd7b204d5f4da9 100644 (file)
@@ -1,20 +1,25 @@
 #ifndef NO_BCACHEFS_CHARDEV
 
 #include "bcachefs.h"
+#include "alloc.h"
 #include "bcachefs_ioctl.h"
 #include "buckets.h"
 #include "chardev.h"
+#include "move.h"
 #include "super.h"
 #include "super-io.h"
 
-#include <linux/module.h>
-#include <linux/fs.h>
-#include <linux/major.h>
+#include <linux/anon_inodes.h>
 #include <linux/cdev.h>
 #include <linux/device.h>
+#include <linux/file.h>
+#include <linux/fs.h>
 #include <linux/ioctl.h>
-#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/major.h>
+#include <linux/sched/task.h>
 #include <linux/slab.h>
+#include <linux/uaccess.h>
 
 /* returns with ref on ca->ref */
 static struct bch_dev *bch2_device_lookup(struct bch_fs *c, u64 dev,
@@ -35,36 +40,25 @@ static struct bch_dev *bch2_device_lookup(struct bch_fs *c, u64 dev,
                if (!ca)
                        return ERR_PTR(-EINVAL);
        } else {
-               struct block_device *bdev;
                char *path;
-               unsigned i;
 
                path = strndup_user((const char __user *)
                                    (unsigned long) dev, PATH_MAX);
                if (IS_ERR(path))
                        return ERR_CAST(path);
 
-               bdev = lookup_bdev(path);
+               ca = bch2_dev_lookup(c, path);
                kfree(path);
-               if (IS_ERR(bdev))
-                       return ERR_CAST(bdev);
-
-               for_each_member_device(ca, c, i)
-                       if (ca->disk_sb.bdev == bdev)
-                               goto found;
-
-               ca = ERR_PTR(-ENOENT);
-found:
-               bdput(bdev);
        }
 
        return ca;
 }
 
+#if 0
 static long bch2_ioctl_assemble(struct bch_ioctl_assemble __user *user_arg)
 {
        struct bch_ioctl_assemble arg;
-       const char *err;
+       struct bch_fs *c;
        u64 *user_devs = NULL;
        char **devs = NULL;
        unsigned i;
@@ -96,14 +90,10 @@ static long bch2_ioctl_assemble(struct bch_ioctl_assemble __user *user_arg)
                }
        }
 
-       err = bch2_fs_open(devs, arg.nr_devs, bch2_opts_empty(), NULL);
-       if (err) {
-               pr_err("Could not open filesystem: %s", err);
-               ret = -EINVAL;
-               goto err;
-       }
-
-       ret = 0;
+       c = bch2_fs_open(devs, arg.nr_devs, bch2_opts_empty());
+       ret = PTR_ERR_OR_ZERO(c);
+       if (!ret)
+               closure_put(&c->cl);
 err:
        if (devs)
                for (i = 0; i < arg.nr_devs; i++)
@@ -138,14 +128,17 @@ static long bch2_ioctl_incremental(struct bch_ioctl_incremental __user *user_arg
 
        return 0;
 }
+#endif
 
 static long bch2_global_ioctl(unsigned cmd, void __user *arg)
 {
        switch (cmd) {
+#if 0
        case BCH_IOCTL_ASSEMBLE:
                return bch2_ioctl_assemble(arg);
        case BCH_IOCTL_INCREMENTAL:
                return bch2_ioctl_incremental(arg);
+#endif
        default:
                return -ENOTTY;
        }
@@ -159,6 +152,7 @@ static long bch2_ioctl_query_uuid(struct bch_fs *c,
                            sizeof(c->sb.user_uuid));
 }
 
+#if 0
 static long bch2_ioctl_start(struct bch_fs *c, struct bch_ioctl_start arg)
 {
        if (arg.flags || arg.pad)
@@ -172,6 +166,7 @@ static long bch2_ioctl_stop(struct bch_fs *c)
        bch2_fs_stop(c);
        return 0;
 }
+#endif
 
 static long bch2_ioctl_disk_add(struct bch_fs *c, struct bch_ioctl_disk arg)
 {
@@ -270,23 +265,109 @@ static long bch2_ioctl_disk_set_state(struct bch_fs *c,
        return ret;
 }
 
-static long bch2_ioctl_disk_evacuate(struct bch_fs *c,
-                                    struct bch_ioctl_disk arg)
+struct bch_data_ctx {
+       struct bch_fs                   *c;
+       struct bch_ioctl_data           arg;
+       struct bch_move_stats           stats;
+
+       int                             ret;
+
+       struct task_struct              *thread;
+};
+
+static int bch2_data_thread(void *arg)
+{
+       struct bch_data_ctx *ctx = arg;
+
+       ctx->ret = bch2_data_job(ctx->c, &ctx->stats, ctx->arg);
+
+       ctx->stats.data_type = U8_MAX;
+       return 0;
+}
+
+static int bch2_data_job_release(struct inode *inode, struct file *file)
 {
-       struct bch_dev *ca;
-       int ret;
+       struct bch_data_ctx *ctx = file->private_data;
 
-       if ((arg.flags & ~BCH_BY_INDEX) ||
-           arg.pad)
+       kthread_stop(ctx->thread);
+       put_task_struct(ctx->thread);
+       kfree(ctx);
+       return 0;
+}
+
+static ssize_t bch2_data_job_read(struct file *file, char __user *buf,
+                                 size_t len, loff_t *ppos)
+{
+       struct bch_data_ctx *ctx = file->private_data;
+       struct bch_fs *c = ctx->c;
+       struct bch_ioctl_data_event e = {
+               .type                   = BCH_DATA_EVENT_PROGRESS,
+               .p.data_type            = ctx->stats.data_type,
+               .p.btree_id             = ctx->stats.iter.btree_id,
+               .p.pos                  = ctx->stats.iter.pos,
+               .p.sectors_done         = atomic64_read(&ctx->stats.sectors_seen),
+               .p.sectors_total        = bch2_fs_sectors_used(c, bch2_fs_usage_read(c)),
+       };
+
+       if (len < sizeof(e))
                return -EINVAL;
 
-       ca = bch2_device_lookup(c, arg.dev, arg.flags);
-       if (IS_ERR(ca))
-               return PTR_ERR(ca);
+       return copy_to_user(buf, &e, sizeof(e)) ?: sizeof(e);
+}
 
-       ret = bch2_dev_evacuate(c, ca);
+static const struct file_operations bcachefs_data_ops = {
+       .release        = bch2_data_job_release,
+       .read           = bch2_data_job_read,
+       .llseek         = no_llseek,
+};
 
-       percpu_ref_put(&ca->ref);
+static long bch2_ioctl_data(struct bch_fs *c,
+                           struct bch_ioctl_data arg)
+{
+       struct bch_data_ctx *ctx = NULL;
+       struct file *file = NULL;
+       unsigned flags = O_RDONLY|O_CLOEXEC|O_NONBLOCK;
+       int ret, fd = -1;
+
+       if (arg.op >= BCH_DATA_OP_NR || arg.flags)
+               return -EINVAL;
+
+       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+       if (!ctx)
+               return -ENOMEM;
+
+       ctx->c = c;
+       ctx->arg = arg;
+
+       ctx->thread = kthread_create(bch2_data_thread, ctx, "[bcachefs]");
+       if (IS_ERR(ctx->thread)) {
+               ret = PTR_ERR(ctx->thread);
+               goto err;
+       }
+
+       ret = get_unused_fd_flags(flags);
+       if (ret < 0)
+               goto err;
+       fd = ret;
+
+       file = anon_inode_getfile("[bcachefs]", &bcachefs_data_ops, ctx, flags);
+       if (IS_ERR(file)) {
+               ret = PTR_ERR(file);
+               goto err;
+       }
+
+       fd_install(fd, file);
+
+       get_task_struct(ctx->thread);
+       wake_up_process(ctx->thread);
+
+       return fd;
+err:
+       if (fd >= 0)
+               put_unused_fd(fd);
+       if (!IS_ERR_OR_NULL(ctx->thread))
+               kthread_stop(ctx->thread);
+       kfree(ctx);
        return ret;
 }
 
@@ -298,6 +379,9 @@ static long bch2_ioctl_usage(struct bch_fs *c,
        unsigned i, j;
        int ret;
 
+       if (!test_bit(BCH_FS_STARTED, &c->flags))
+               return -EINVAL;
+
        if (copy_from_user(&arg, user_arg, sizeof(arg)))
                return -EFAULT;
 
@@ -342,7 +426,7 @@ static long bch2_ioctl_usage(struct bch_fs *c,
 
                if (ca->dev_idx >= arg.nr_devices) {
                        percpu_ref_put(&ca->ref);
-                       return -ENOSPC;
+                       return -ERANGE;
                }
 
                if (percpu_ref_tryget(&ca->io_ref)) {
@@ -363,6 +447,82 @@ static long bch2_ioctl_usage(struct bch_fs *c,
        return 0;
 }
 
+static long bch2_ioctl_read_super(struct bch_fs *c,
+                                 struct bch_ioctl_read_super arg)
+{
+       struct bch_dev *ca = NULL;
+       struct bch_sb *sb;
+       int ret = 0;
+
+       if ((arg.flags & ~(BCH_BY_INDEX|BCH_READ_DEV)) ||
+           arg.pad)
+               return -EINVAL;
+
+       mutex_lock(&c->sb_lock);
+
+       if (arg.flags & BCH_READ_DEV) {
+               ca = bch2_device_lookup(c, arg.dev, arg.flags);
+
+               if (IS_ERR(ca)) {
+                       ret = PTR_ERR(ca);
+                       goto err;
+               }
+
+               sb = ca->disk_sb.sb;
+       } else {
+               sb = c->disk_sb.sb;
+       }
+
+       if (vstruct_bytes(sb) > arg.size) {
+               ret = -ERANGE;
+               goto err;
+       }
+
+       ret = copy_to_user((void __user *)(unsigned long)arg.sb,
+                          sb, vstruct_bytes(sb));
+err:
+       if (ca)
+               percpu_ref_put(&ca->ref);
+       mutex_unlock(&c->sb_lock);
+       return ret;
+}
+
+static long bch2_ioctl_disk_get_idx(struct bch_fs *c,
+                                   struct bch_ioctl_disk_get_idx arg)
+{
+       dev_t dev = huge_decode_dev(arg.dev);
+       struct bch_dev *ca;
+       unsigned i;
+
+       for_each_online_member(ca, c, i)
+               if (ca->disk_sb.bdev->bd_dev == dev) {
+                       percpu_ref_put(&ca->io_ref);
+                       return i;
+               }
+
+       return -ENOENT;
+}
+
+static long bch2_ioctl_disk_resize(struct bch_fs *c,
+                                  struct bch_ioctl_disk_resize arg)
+{
+       struct bch_dev *ca;
+       int ret;
+
+       if ((arg.flags & ~BCH_BY_INDEX) ||
+           arg.pad)
+               return -EINVAL;
+
+       ca = bch2_device_lookup(c, arg.dev, arg.flags);
+       if (IS_ERR(ca))
+               return PTR_ERR(ca);
+
+       ret = bch2_dev_resize(c, ca, arg.nbuckets);
+
+       percpu_ref_put(&ca->ref);
+       return ret;
+}
+
 #define BCH_IOCTL(_name, _argtype)                                     \
 do {                                                                   \
        _argtype i;                                                     \
@@ -385,13 +545,24 @@ long bch2_fs_ioctl(struct bch_fs *c, unsigned cmd, void __user *arg)
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
 
-       /* ioctls that do require admin cap: */
        switch (cmd) {
+#if 0
        case BCH_IOCTL_START:
                BCH_IOCTL(start, struct bch_ioctl_start);
        case BCH_IOCTL_STOP:
                return bch2_ioctl_stop(c);
+#endif
+       case BCH_IOCTL_READ_SUPER:
+               BCH_IOCTL(read_super, struct bch_ioctl_read_super);
+       case BCH_IOCTL_DISK_GET_IDX:
+               BCH_IOCTL(disk_get_idx, struct bch_ioctl_disk_get_idx);
+       }
 
+       if (!test_bit(BCH_FS_STARTED, &c->flags))
+               return -EINVAL;
+
+       /* ioctls that do require admin cap: */
+       switch (cmd) {
        case BCH_IOCTL_DISK_ADD:
                BCH_IOCTL(disk_add, struct bch_ioctl_disk);
        case BCH_IOCTL_DISK_REMOVE:
@@ -402,17 +573,22 @@ long bch2_fs_ioctl(struct bch_fs *c, unsigned cmd, void __user *arg)
                BCH_IOCTL(disk_offline, struct bch_ioctl_disk);
        case BCH_IOCTL_DISK_SET_STATE:
                BCH_IOCTL(disk_set_state, struct bch_ioctl_disk_set_state);
-       case BCH_IOCTL_DISK_EVACUATE:
-               BCH_IOCTL(disk_evacuate, struct bch_ioctl_disk);
+       case BCH_IOCTL_DATA:
+               BCH_IOCTL(data, struct bch_ioctl_data);
+       case BCH_IOCTL_DISK_RESIZE:
+               BCH_IOCTL(disk_resize, struct bch_ioctl_disk_resize);
 
        default:
                return -ENOTTY;
        }
 }
 
+static DEFINE_IDR(bch_chardev_minor);
+
 static long bch2_chardev_ioctl(struct file *filp, unsigned cmd, unsigned long v)
 {
-       struct bch_fs *c = filp->private_data;
+       unsigned minor = iminor(file_inode(filp));
+       struct bch_fs *c = minor < U8_MAX ? idr_find(&bch_chardev_minor, minor) : NULL;
        void __user *arg = (void __user *) v;
 
        return c
@@ -429,7 +605,6 @@ static const struct file_operations bch_chardev_fops = {
 static int bch_chardev_major;
 static struct class *bch_chardev_class;
 static struct device *bch_chardev;
-static DEFINE_IDR(bch_chardev_minor);
 
 void bch2_fs_chardev_exit(struct bch_fs *c)
 {
@@ -446,7 +621,7 @@ int bch2_fs_chardev_init(struct bch_fs *c)
                return c->minor;
 
        c->chardev = device_create(bch_chardev_class, NULL,
-                                  MKDEV(bch_chardev_major, c->minor), NULL,
+                                  MKDEV(bch_chardev_major, c->minor), c,
                                   "bcachefs%u-ctl", c->minor);
        if (IS_ERR(c->chardev))
                return PTR_ERR(c->chardev);
@@ -458,7 +633,7 @@ void bch2_chardev_exit(void)
 {
        if (!IS_ERR_OR_NULL(bch_chardev_class))
                device_destroy(bch_chardev_class,
-                              MKDEV(bch_chardev_major, 255));
+                              MKDEV(bch_chardev_major, U8_MAX));
        if (!IS_ERR_OR_NULL(bch_chardev_class))
                class_destroy(bch_chardev_class);
        if (bch_chardev_major > 0)
@@ -476,7 +651,7 @@ int __init bch2_chardev_init(void)
                return PTR_ERR(bch_chardev_class);
 
        bch_chardev = device_create(bch_chardev_class, NULL,
-                                   MKDEV(bch_chardev_major, 255),
+                                   MKDEV(bch_chardev_major, U8_MAX),
                                    NULL, "bcachefs-ctl");
        if (IS_ERR(bch_chardev))
                return PTR_ERR(bch_chardev);