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