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