]> git.sesse.net Git - bcachefs-tools-debian/blob - libbcachefs/chardev.c
Update bcachefs sources to 90d78c2461 bcachefs: Option parsing for io targets
[bcachefs-tools-debian] / libbcachefs / chardev.c
1 #ifndef NO_BCACHEFS_CHARDEV
2
3 #include "bcachefs.h"
4 #include "alloc.h"
5 #include "bcachefs_ioctl.h"
6 #include "buckets.h"
7 #include "chardev.h"
8 #include "move.h"
9 #include "super.h"
10 #include "super-io.h"
11
12 #include <linux/anon_inodes.h>
13 #include <linux/cdev.h>
14 #include <linux/device.h>
15 #include <linux/file.h>
16 #include <linux/fs.h>
17 #include <linux/ioctl.h>
18 #include <linux/kthread.h>
19 #include <linux/major.h>
20 #include <linux/sched/task.h>
21 #include <linux/slab.h>
22 #include <linux/uaccess.h>
23
24 /* returns with ref on ca->ref */
25 static struct bch_dev *bch2_device_lookup(struct bch_fs *c, u64 dev,
26                                           unsigned flags)
27 {
28         struct bch_dev *ca;
29
30         if (flags & BCH_BY_INDEX) {
31                 if (dev >= c->sb.nr_devices)
32                         return ERR_PTR(-EINVAL);
33
34                 rcu_read_lock();
35                 ca = rcu_dereference(c->devs[dev]);
36                 if (ca)
37                         percpu_ref_get(&ca->ref);
38                 rcu_read_unlock();
39
40                 if (!ca)
41                         return ERR_PTR(-EINVAL);
42         } else {
43                 char *path;
44
45                 path = strndup_user((const char __user *)
46                                     (unsigned long) dev, PATH_MAX);
47                 if (IS_ERR(path))
48                         return ERR_CAST(path);
49
50                 ca = bch2_dev_lookup(c, path);
51                 kfree(path);
52         }
53
54         return ca;
55 }
56
57 static long bch2_ioctl_assemble(struct bch_ioctl_assemble __user *user_arg)
58 {
59         struct bch_ioctl_assemble arg;
60         struct bch_fs *c;
61         u64 *user_devs = NULL;
62         char **devs = NULL;
63         unsigned i;
64         int ret = -EFAULT;
65
66         if (copy_from_user(&arg, user_arg, sizeof(arg)))
67                 return -EFAULT;
68
69         if (arg.flags || arg.pad)
70                 return -EINVAL;
71
72         user_devs = kmalloc_array(arg.nr_devs, sizeof(u64), GFP_KERNEL);
73         if (!user_devs)
74                 return -ENOMEM;
75
76         devs = kcalloc(arg.nr_devs, sizeof(char *), GFP_KERNEL);
77
78         if (copy_from_user(user_devs, user_arg->devs,
79                            sizeof(u64) * arg.nr_devs))
80                 goto err;
81
82         for (i = 0; i < arg.nr_devs; i++) {
83                 devs[i] = strndup_user((const char __user *)(unsigned long)
84                                        user_devs[i],
85                                        PATH_MAX);
86                 if (!devs[i]) {
87                         ret = -ENOMEM;
88                         goto err;
89                 }
90         }
91
92         c = bch2_fs_open(devs, arg.nr_devs, bch2_opts_empty());
93         ret = PTR_ERR_OR_ZERO(c);
94         if (!ret)
95                 closure_put(&c->cl);
96 err:
97         if (devs)
98                 for (i = 0; i < arg.nr_devs; i++)
99                         kfree(devs[i]);
100         kfree(devs);
101         return ret;
102 }
103
104 static long bch2_ioctl_incremental(struct bch_ioctl_incremental __user *user_arg)
105 {
106         struct bch_ioctl_incremental arg;
107         const char *err;
108         char *path;
109
110         if (copy_from_user(&arg, user_arg, sizeof(arg)))
111                 return -EFAULT;
112
113         if (arg.flags || arg.pad)
114                 return -EINVAL;
115
116         path = strndup_user((const char __user *)(unsigned long) arg.dev, PATH_MAX);
117         if (!path)
118                 return -ENOMEM;
119
120         err = bch2_fs_open_incremental(path);
121         kfree(path);
122
123         if (err) {
124                 pr_err("Could not register bcachefs devices: %s", err);
125                 return -EINVAL;
126         }
127
128         return 0;
129 }
130
131 static long bch2_global_ioctl(unsigned cmd, void __user *arg)
132 {
133         switch (cmd) {
134         case BCH_IOCTL_ASSEMBLE:
135                 return bch2_ioctl_assemble(arg);
136         case BCH_IOCTL_INCREMENTAL:
137                 return bch2_ioctl_incremental(arg);
138         default:
139                 return -ENOTTY;
140         }
141 }
142
143 static long bch2_ioctl_query_uuid(struct bch_fs *c,
144                         struct bch_ioctl_query_uuid __user *user_arg)
145 {
146         return copy_to_user(&user_arg->uuid,
147                             &c->sb.user_uuid,
148                             sizeof(c->sb.user_uuid));
149 }
150
151 static long bch2_ioctl_start(struct bch_fs *c, struct bch_ioctl_start arg)
152 {
153         if (arg.flags || arg.pad)
154                 return -EINVAL;
155
156         return bch2_fs_start(c) ? -EIO : 0;
157 }
158
159 static long bch2_ioctl_stop(struct bch_fs *c)
160 {
161         bch2_fs_stop(c);
162         return 0;
163 }
164
165 static long bch2_ioctl_disk_add(struct bch_fs *c, struct bch_ioctl_disk arg)
166 {
167         char *path;
168         int ret;
169
170         if (arg.flags || arg.pad)
171                 return -EINVAL;
172
173         path = strndup_user((const char __user *)(unsigned long) arg.dev, PATH_MAX);
174         if (!path)
175                 return -ENOMEM;
176
177         ret = bch2_dev_add(c, path);
178         kfree(path);
179
180         return ret;
181 }
182
183 static long bch2_ioctl_disk_remove(struct bch_fs *c, struct bch_ioctl_disk arg)
184 {
185         struct bch_dev *ca;
186
187         if ((arg.flags & ~(BCH_FORCE_IF_DATA_LOST|
188                            BCH_FORCE_IF_METADATA_LOST|
189                            BCH_FORCE_IF_DEGRADED|
190                            BCH_BY_INDEX)) ||
191             arg.pad)
192                 return -EINVAL;
193
194         ca = bch2_device_lookup(c, arg.dev, arg.flags);
195         if (IS_ERR(ca))
196                 return PTR_ERR(ca);
197
198         return bch2_dev_remove(c, ca, arg.flags);
199 }
200
201 static long bch2_ioctl_disk_online(struct bch_fs *c, struct bch_ioctl_disk arg)
202 {
203         char *path;
204         int ret;
205
206         if (arg.flags || arg.pad)
207                 return -EINVAL;
208
209         path = strndup_user((const char __user *)(unsigned long) arg.dev, PATH_MAX);
210         if (!path)
211                 return -ENOMEM;
212
213         ret = bch2_dev_online(c, path);
214         kfree(path);
215         return ret;
216 }
217
218 static long bch2_ioctl_disk_offline(struct bch_fs *c, struct bch_ioctl_disk arg)
219 {
220         struct bch_dev *ca;
221         int ret;
222
223         if ((arg.flags & ~(BCH_FORCE_IF_DATA_LOST|
224                            BCH_FORCE_IF_METADATA_LOST|
225                            BCH_FORCE_IF_DEGRADED|
226                            BCH_BY_INDEX)) ||
227             arg.pad)
228                 return -EINVAL;
229
230         ca = bch2_device_lookup(c, arg.dev, arg.flags);
231         if (IS_ERR(ca))
232                 return PTR_ERR(ca);
233
234         ret = bch2_dev_offline(c, ca, arg.flags);
235         percpu_ref_put(&ca->ref);
236         return ret;
237 }
238
239 static long bch2_ioctl_disk_set_state(struct bch_fs *c,
240                         struct bch_ioctl_disk_set_state arg)
241 {
242         struct bch_dev *ca;
243         int ret;
244
245         if ((arg.flags & ~(BCH_FORCE_IF_DATA_LOST|
246                            BCH_FORCE_IF_METADATA_LOST|
247                            BCH_FORCE_IF_DEGRADED|
248                            BCH_BY_INDEX)) ||
249             arg.pad[0] || arg.pad[1] || arg.pad[2])
250                 return -EINVAL;
251
252         ca = bch2_device_lookup(c, arg.dev, arg.flags);
253         if (IS_ERR(ca))
254                 return PTR_ERR(ca);
255
256         ret = bch2_dev_set_state(c, ca, arg.new_state, arg.flags);
257
258         percpu_ref_put(&ca->ref);
259         return ret;
260 }
261
262 struct bch_data_ctx {
263         struct bch_fs                   *c;
264         struct bch_ioctl_data           arg;
265         struct bch_move_stats           stats;
266
267         int                             ret;
268
269         struct task_struct              *thread;
270 };
271
272 static int bch2_data_thread(void *arg)
273 {
274         struct bch_data_ctx *ctx = arg;
275
276         ctx->ret = bch2_data_job(ctx->c, &ctx->stats, ctx->arg);
277
278         ctx->stats.data_type = U8_MAX;
279         return 0;
280 }
281
282 static int bch2_data_job_release(struct inode *inode, struct file *file)
283 {
284         struct bch_data_ctx *ctx = file->private_data;
285
286         kthread_stop(ctx->thread);
287         put_task_struct(ctx->thread);
288         kfree(ctx);
289         return 0;
290 }
291
292 static ssize_t bch2_data_job_read(struct file *file, char __user *buf,
293                                   size_t len, loff_t *ppos)
294 {
295         struct bch_data_ctx *ctx = file->private_data;
296         struct bch_fs *c = ctx->c;
297         struct bch_ioctl_data_progress p = {
298                 .data_type      = ctx->stats.data_type,
299                 .btree_id       = ctx->stats.iter.btree_id,
300                 .pos            = ctx->stats.iter.pos,
301                 .sectors_done   = atomic64_read(&ctx->stats.sectors_seen),
302                 .sectors_total  = bch2_fs_sectors_used(c, bch2_fs_usage_read(c)),
303         };
304
305         if (len != sizeof(p))
306                 return -EINVAL;
307
308         return copy_to_user(buf, &p, sizeof(p)) ?: sizeof(p);
309 }
310
311 static const struct file_operations bcachefs_data_ops = {
312         .release        = bch2_data_job_release,
313         .read           = bch2_data_job_read,
314         .llseek         = no_llseek,
315 };
316
317 static long bch2_ioctl_data(struct bch_fs *c,
318                             struct bch_ioctl_data arg)
319 {
320         struct bch_data_ctx *ctx = NULL;
321         struct file *file = NULL;
322         unsigned flags = O_RDONLY|O_CLOEXEC|O_NONBLOCK;
323         int ret, fd = -1;
324
325         if (arg.op >= BCH_DATA_OP_NR || arg.flags)
326                 return -EINVAL;
327
328         ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
329         if (!ctx)
330                 return -ENOMEM;
331
332         ctx->c = c;
333         ctx->arg = arg;
334
335         ctx->thread = kthread_create(bch2_data_thread, ctx, "[bcachefs]");
336         if (IS_ERR(ctx->thread)) {
337                 ret = PTR_ERR(ctx->thread);
338                 goto err;
339         }
340
341         ret = get_unused_fd_flags(flags);
342         if (ret < 0)
343                 goto err;
344         fd = ret;
345
346         file = anon_inode_getfile("[bcachefs]", &bcachefs_data_ops, ctx, flags);
347         if (IS_ERR(file)) {
348                 ret = PTR_ERR(file);
349                 goto err;
350         }
351
352         fd_install(fd, file);
353
354         get_task_struct(ctx->thread);
355         wake_up_process(ctx->thread);
356
357         return fd;
358 err:
359         if (fd >= 0)
360                 put_unused_fd(fd);
361         if (!IS_ERR_OR_NULL(ctx->thread))
362                 kthread_stop(ctx->thread);
363         kfree(ctx);
364         return ret;
365 }
366
367 static long bch2_ioctl_usage(struct bch_fs *c,
368                              struct bch_ioctl_usage __user *user_arg)
369 {
370         struct bch_ioctl_usage arg;
371         struct bch_dev *ca;
372         unsigned i, j;
373         int ret;
374
375         if (copy_from_user(&arg, user_arg, sizeof(arg)))
376                 return -EFAULT;
377
378         for (i = 0; i < arg.nr_devices; i++) {
379                 struct bch_ioctl_dev_usage dst = { .alive = 0 };
380
381                 ret = copy_to_user(&user_arg->devs[i], &dst, sizeof(dst));
382                 if (ret)
383                         return ret;
384         }
385
386         {
387                 struct bch_fs_usage src = bch2_fs_usage_read(c);
388                 struct bch_ioctl_fs_usage dst = {
389                         .capacity               = c->capacity,
390                         .used                   = bch2_fs_sectors_used(c, src),
391                         .online_reserved        = src.online_reserved,
392                 };
393
394                 for (i = 0; i < BCH_REPLICAS_MAX; i++) {
395                         dst.persistent_reserved[i] =
396                                 src.s[i].persistent_reserved;
397
398                         for (j = 0; j < S_ALLOC_NR; j++)
399                                 dst.sectors[s_alloc_to_data_type(j)][i] =
400                                         src.s[i].data[j];
401                 }
402
403                 ret = copy_to_user(&user_arg->fs, &dst, sizeof(dst));
404                 if (ret)
405                         return ret;
406         }
407
408         for_each_member_device(ca, c, i) {
409                 struct bch_dev_usage src = bch2_dev_usage_read(c, ca);
410                 struct bch_ioctl_dev_usage dst = {
411                         .alive          = 1,
412                         .state          = ca->mi.state,
413                         .bucket_size    = ca->mi.bucket_size,
414                         .nr_buckets     = ca->mi.nbuckets - ca->mi.first_bucket,
415                 };
416
417                 if (ca->dev_idx >= arg.nr_devices) {
418                         percpu_ref_put(&ca->ref);
419                         return -ENOSPC;
420                 }
421
422                 if (percpu_ref_tryget(&ca->io_ref)) {
423                         dst.dev = huge_encode_dev(ca->disk_sb.bdev->bd_dev);
424                         percpu_ref_put(&ca->io_ref);
425                 }
426
427                 for (j = 0; j < BCH_DATA_NR; j++) {
428                         dst.buckets[j] = src.buckets[j];
429                         dst.sectors[j] = src.sectors[j];
430                 }
431
432                 ret = copy_to_user(&user_arg->devs[i], &dst, sizeof(dst));
433                 if (ret)
434                         return ret;
435         }
436
437         return 0;
438 }
439
440 static long bch2_ioctl_read_super(struct bch_fs *c,
441                                   struct bch_ioctl_read_super arg)
442 {
443         struct bch_dev *ca = NULL;
444         struct bch_sb *sb;
445         int ret = 0;
446
447         if ((arg.flags & ~(BCH_BY_INDEX|BCH_READ_DEV)) ||
448             arg.pad)
449                 return -EINVAL;
450
451         mutex_lock(&c->sb_lock);
452
453         if (arg.flags & BCH_READ_DEV) {
454                 ca = bch2_device_lookup(c, arg.dev, arg.flags);
455
456                 if (IS_ERR(ca)) {
457                         ret = PTR_ERR(ca);
458                         goto err;
459                 }
460
461                 sb = ca->disk_sb.sb;
462         } else {
463                 sb = c->disk_sb;
464         }
465
466         if (vstruct_bytes(sb) > arg.size) {
467                 ret = -ERANGE;
468                 goto err;
469         }
470
471         ret = copy_to_user((void __user *)(unsigned long)arg.sb,
472                            sb, vstruct_bytes(sb));
473 err:
474         if (ca)
475                 percpu_ref_put(&ca->ref);
476         mutex_unlock(&c->sb_lock);
477         return ret;
478 }
479
480 static long bch2_ioctl_disk_get_idx(struct bch_fs *c,
481                                     struct bch_ioctl_disk_get_idx arg)
482 {
483         dev_t dev = huge_decode_dev(arg.dev);
484         struct bch_dev *ca;
485         unsigned i;
486
487         for_each_online_member(ca, c, i)
488                 if (ca->disk_sb.bdev->bd_dev == dev) {
489                         percpu_ref_put(&ca->io_ref);
490                         return i;
491                 }
492
493         return -ENOENT;
494 }
495
496 static long bch2_ioctl_disk_resize(struct bch_fs *c,
497                                    struct bch_ioctl_disk_resize arg)
498 {
499         struct bch_dev *ca;
500         int ret;
501
502         if ((arg.flags & ~BCH_BY_INDEX) ||
503             arg.pad)
504                 return -EINVAL;
505
506         ca = bch2_device_lookup(c, arg.dev, arg.flags);
507         if (IS_ERR(ca))
508                 return PTR_ERR(ca);
509
510         ret = bch2_dev_resize(c, ca, arg.nbuckets);
511
512         percpu_ref_put(&ca->ref);
513         return ret;
514 }
515
516 #define BCH_IOCTL(_name, _argtype)                                      \
517 do {                                                                    \
518         _argtype i;                                                     \
519                                                                         \
520         if (copy_from_user(&i, arg, sizeof(i)))                         \
521                 return -EFAULT;                                         \
522         return bch2_ioctl_##_name(c, i);                                \
523 } while (0)
524
525 long bch2_fs_ioctl(struct bch_fs *c, unsigned cmd, void __user *arg)
526 {
527         /* ioctls that don't require admin cap: */
528         switch (cmd) {
529         case BCH_IOCTL_QUERY_UUID:
530                 return bch2_ioctl_query_uuid(c, arg);
531         case BCH_IOCTL_USAGE:
532                 return bch2_ioctl_usage(c, arg);
533         }
534
535         if (!capable(CAP_SYS_ADMIN))
536                 return -EPERM;
537
538         /* ioctls that do require admin cap: */
539         switch (cmd) {
540         case BCH_IOCTL_START:
541                 BCH_IOCTL(start, struct bch_ioctl_start);
542         case BCH_IOCTL_STOP:
543                 return bch2_ioctl_stop(c);
544
545         case BCH_IOCTL_DISK_ADD:
546                 BCH_IOCTL(disk_add, struct bch_ioctl_disk);
547         case BCH_IOCTL_DISK_REMOVE:
548                 BCH_IOCTL(disk_remove, struct bch_ioctl_disk);
549         case BCH_IOCTL_DISK_ONLINE:
550                 BCH_IOCTL(disk_online, struct bch_ioctl_disk);
551         case BCH_IOCTL_DISK_OFFLINE:
552                 BCH_IOCTL(disk_offline, struct bch_ioctl_disk);
553         case BCH_IOCTL_DISK_SET_STATE:
554                 BCH_IOCTL(disk_set_state, struct bch_ioctl_disk_set_state);
555         case BCH_IOCTL_DATA:
556                 BCH_IOCTL(data, struct bch_ioctl_data);
557         case BCH_IOCTL_READ_SUPER:
558                 BCH_IOCTL(read_super, struct bch_ioctl_read_super);
559         case BCH_IOCTL_DISK_GET_IDX:
560                 BCH_IOCTL(disk_get_idx, struct bch_ioctl_disk_get_idx);
561         case BCH_IOCTL_DISK_RESIZE:
562                 BCH_IOCTL(disk_resize, struct bch_ioctl_disk_resize);
563
564         default:
565                 return -ENOTTY;
566         }
567 }
568
569 static DEFINE_IDR(bch_chardev_minor);
570
571 static long bch2_chardev_ioctl(struct file *filp, unsigned cmd, unsigned long v)
572 {
573         unsigned minor = iminor(file_inode(filp));
574         struct bch_fs *c = minor < U8_MAX ? idr_find(&bch_chardev_minor, minor) : NULL;
575         void __user *arg = (void __user *) v;
576
577         return c
578                 ? bch2_fs_ioctl(c, cmd, arg)
579                 : bch2_global_ioctl(cmd, arg);
580 }
581
582 static const struct file_operations bch_chardev_fops = {
583         .owner          = THIS_MODULE,
584         .unlocked_ioctl = bch2_chardev_ioctl,
585         .open           = nonseekable_open,
586 };
587
588 static int bch_chardev_major;
589 static struct class *bch_chardev_class;
590 static struct device *bch_chardev;
591
592 void bch2_fs_chardev_exit(struct bch_fs *c)
593 {
594         if (!IS_ERR_OR_NULL(c->chardev))
595                 device_unregister(c->chardev);
596         if (c->minor >= 0)
597                 idr_remove(&bch_chardev_minor, c->minor);
598 }
599
600 int bch2_fs_chardev_init(struct bch_fs *c)
601 {
602         c->minor = idr_alloc(&bch_chardev_minor, c, 0, 0, GFP_KERNEL);
603         if (c->minor < 0)
604                 return c->minor;
605
606         c->chardev = device_create(bch_chardev_class, NULL,
607                                    MKDEV(bch_chardev_major, c->minor), c,
608                                    "bcachefs%u-ctl", c->minor);
609         if (IS_ERR(c->chardev))
610                 return PTR_ERR(c->chardev);
611
612         return 0;
613 }
614
615 void bch2_chardev_exit(void)
616 {
617         if (!IS_ERR_OR_NULL(bch_chardev_class))
618                 device_destroy(bch_chardev_class,
619                                MKDEV(bch_chardev_major, U8_MAX));
620         if (!IS_ERR_OR_NULL(bch_chardev_class))
621                 class_destroy(bch_chardev_class);
622         if (bch_chardev_major > 0)
623                 unregister_chrdev(bch_chardev_major, "bcachefs");
624 }
625
626 int __init bch2_chardev_init(void)
627 {
628         bch_chardev_major = register_chrdev(0, "bcachefs-ctl", &bch_chardev_fops);
629         if (bch_chardev_major < 0)
630                 return bch_chardev_major;
631
632         bch_chardev_class = class_create(THIS_MODULE, "bcachefs");
633         if (IS_ERR(bch_chardev_class))
634                 return PTR_ERR(bch_chardev_class);
635
636         bch_chardev = device_create(bch_chardev_class, NULL,
637                                     MKDEV(bch_chardev_major, U8_MAX),
638                                     NULL, "bcachefs-ctl");
639         if (IS_ERR(bch_chardev))
640                 return PTR_ERR(bch_chardev);
641
642         return 0;
643 }
644
645 #endif /* NO_BCACHEFS_CHARDEV */