]> git.sesse.net Git - bcachefs-tools-debian/blob - libbcachefs/chardev.c
Update bcachefs sources to 2afdc642c2 bcachefs: kill bucket_data_type, improve disk...
[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 #define BCH_IOCTL(_name, _argtype)                                      \
367 do {                                                                    \
368         _argtype i;                                                     \
369                                                                         \
370         if (copy_from_user(&i, arg, sizeof(i)))                         \
371                 return -EFAULT;                                         \
372         return bch2_ioctl_##_name(c, i);                                \
373 } while (0)
374
375 long bch2_fs_ioctl(struct bch_fs *c, unsigned cmd, void __user *arg)
376 {
377         /* ioctls that don't require admin cap: */
378         switch (cmd) {
379         case BCH_IOCTL_QUERY_UUID:
380                 return bch2_ioctl_query_uuid(c, arg);
381         case BCH_IOCTL_USAGE:
382                 return bch2_ioctl_usage(c, arg);
383         }
384
385         if (!capable(CAP_SYS_ADMIN))
386                 return -EPERM;
387
388         /* ioctls that do require admin cap: */
389         switch (cmd) {
390         case BCH_IOCTL_START:
391                 BCH_IOCTL(start, struct bch_ioctl_start);
392         case BCH_IOCTL_STOP:
393                 return bch2_ioctl_stop(c);
394
395         case BCH_IOCTL_DISK_ADD:
396                 BCH_IOCTL(disk_add, struct bch_ioctl_disk);
397         case BCH_IOCTL_DISK_REMOVE:
398                 BCH_IOCTL(disk_remove, struct bch_ioctl_disk);
399         case BCH_IOCTL_DISK_ONLINE:
400                 BCH_IOCTL(disk_online, struct bch_ioctl_disk);
401         case BCH_IOCTL_DISK_OFFLINE:
402                 BCH_IOCTL(disk_offline, struct bch_ioctl_disk);
403         case BCH_IOCTL_DISK_SET_STATE:
404                 BCH_IOCTL(disk_set_state, struct bch_ioctl_disk_set_state);
405         case BCH_IOCTL_DISK_EVACUATE:
406                 BCH_IOCTL(disk_evacuate, struct bch_ioctl_disk);
407
408         default:
409                 return -ENOTTY;
410         }
411 }
412
413 static long bch2_chardev_ioctl(struct file *filp, unsigned cmd, unsigned long v)
414 {
415         struct bch_fs *c = filp->private_data;
416         void __user *arg = (void __user *) v;
417
418         return c
419                 ? bch2_fs_ioctl(c, cmd, arg)
420                 : bch2_global_ioctl(cmd, arg);
421 }
422
423 static const struct file_operations bch_chardev_fops = {
424         .owner          = THIS_MODULE,
425         .unlocked_ioctl = bch2_chardev_ioctl,
426         .open           = nonseekable_open,
427 };
428
429 static int bch_chardev_major;
430 static struct class *bch_chardev_class;
431 static struct device *bch_chardev;
432 static DEFINE_IDR(bch_chardev_minor);
433
434 void bch2_fs_chardev_exit(struct bch_fs *c)
435 {
436         if (!IS_ERR_OR_NULL(c->chardev))
437                 device_unregister(c->chardev);
438         if (c->minor >= 0)
439                 idr_remove(&bch_chardev_minor, c->minor);
440 }
441
442 int bch2_fs_chardev_init(struct bch_fs *c)
443 {
444         c->minor = idr_alloc(&bch_chardev_minor, c, 0, 0, GFP_KERNEL);
445         if (c->minor < 0)
446                 return c->minor;
447
448         c->chardev = device_create(bch_chardev_class, NULL,
449                                    MKDEV(bch_chardev_major, c->minor), NULL,
450                                    "bcachefs%u-ctl", c->minor);
451         if (IS_ERR(c->chardev))
452                 return PTR_ERR(c->chardev);
453
454         return 0;
455 }
456
457 void bch2_chardev_exit(void)
458 {
459         if (!IS_ERR_OR_NULL(bch_chardev_class))
460                 device_destroy(bch_chardev_class,
461                                MKDEV(bch_chardev_major, 255));
462         if (!IS_ERR_OR_NULL(bch_chardev_class))
463                 class_destroy(bch_chardev_class);
464         if (bch_chardev_major > 0)
465                 unregister_chrdev(bch_chardev_major, "bcachefs");
466 }
467
468 int __init bch2_chardev_init(void)
469 {
470         bch_chardev_major = register_chrdev(0, "bcachefs-ctl", &bch_chardev_fops);
471         if (bch_chardev_major < 0)
472                 return bch_chardev_major;
473
474         bch_chardev_class = class_create(THIS_MODULE, "bcachefs");
475         if (IS_ERR(bch_chardev_class))
476                 return PTR_ERR(bch_chardev_class);
477
478         bch_chardev = device_create(bch_chardev_class, NULL,
479                                     MKDEV(bch_chardev_major, 255),
480                                     NULL, "bcachefs-ctl");
481         if (IS_ERR(bch_chardev))
482                 return PTR_ERR(bch_chardev);
483
484         return 0;
485 }
486
487 #endif /* NO_BCACHEFS_CHARDEV */