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