]> git.sesse.net Git - bcachefs-tools-debian/blob - libbcache/chardev.c
Delete more unused shim code, update bcache code
[bcachefs-tools-debian] / libbcache / chardev.c
1 /*
2  * This file adds support for a character device /dev/bcache that is used to
3  * atomically register a list of devices, remove a device from a cache_set
4  * and add a device to a cache set.
5  *
6  * Copyright (c) 2014 Datera, Inc.
7  *
8  */
9
10 #include "bcache.h"
11 #include "super.h"
12 #include "super-io.h"
13
14 #include <linux/module.h>
15 #include <linux/fs.h>
16 #include <linux/major.h>
17 #include <linux/cdev.h>
18 #include <linux/device.h>
19 #include <linux/ioctl.h>
20 #include <linux/uaccess.h>
21 #include <linux/slab.h>
22 #include <linux/bcache-ioctl.h>
23
24 static long bch_ioctl_assemble(struct bch_ioctl_assemble __user *user_arg)
25 {
26         struct bch_ioctl_assemble arg;
27         const char *err;
28         u64 *user_devs = NULL;
29         char **devs = NULL;
30         unsigned i;
31         int ret = -EFAULT;
32
33         if (copy_from_user(&arg, user_arg, sizeof(arg)))
34                 return -EFAULT;
35
36         user_devs = kmalloc_array(arg.nr_devs, sizeof(u64), GFP_KERNEL);
37         if (!devs)
38                 return -ENOMEM;
39
40         devs = kcalloc(arg.nr_devs, sizeof(char *), GFP_KERNEL);
41
42         if (copy_from_user(user_devs, user_arg->devs,
43                            sizeof(u64) * arg.nr_devs))
44                 goto err;
45
46         for (i = 0; i < arg.nr_devs; i++) {
47                 devs[i] = strndup_user((const char __user *)(unsigned long)
48                                        user_devs[i],
49                                        PATH_MAX);
50                 if (!devs[i]) {
51                         ret = -ENOMEM;
52                         goto err;
53                 }
54         }
55
56         err = bch_fs_open(devs, arg.nr_devs, bch_opts_empty(), NULL);
57         if (err) {
58                 pr_err("Could not register cache set: %s", err);
59                 ret = -EINVAL;
60                 goto err;
61         }
62
63         ret = 0;
64 err:
65         if (devs)
66                 for (i = 0; i < arg.nr_devs; i++)
67                         kfree(devs[i]);
68         kfree(devs);
69         return ret;
70 }
71
72 static long bch_ioctl_incremental(struct bch_ioctl_incremental __user *user_arg)
73 {
74         struct bch_ioctl_incremental arg;
75         const char *err;
76         char *path;
77
78         if (copy_from_user(&arg, user_arg, sizeof(arg)))
79                 return -EFAULT;
80
81         path = strndup_user((const char __user *)(unsigned long) arg.dev, PATH_MAX);
82         if (!path)
83                 return -ENOMEM;
84
85         err = bch_fs_open_incremental(path);
86         kfree(path);
87
88         if (err) {
89                 pr_err("Could not register bcache devices: %s", err);
90                 return -EINVAL;
91         }
92
93         return 0;
94 }
95
96 static long bch_global_ioctl(unsigned cmd, void __user *arg)
97 {
98         switch (cmd) {
99         case BCH_IOCTL_ASSEMBLE:
100                 return bch_ioctl_assemble(arg);
101         case BCH_IOCTL_INCREMENTAL:
102                 return bch_ioctl_incremental(arg);
103         default:
104                 return -ENOTTY;
105         }
106 }
107
108 static long bch_ioctl_stop(struct cache_set *c)
109 {
110         bch_fs_stop(c);
111         return 0;
112 }
113
114 static long bch_ioctl_disk_add(struct cache_set *c,
115                                struct bch_ioctl_disk_add __user *user_arg)
116 {
117         struct bch_ioctl_disk_add arg;
118         char *path;
119         int ret;
120
121         if (copy_from_user(&arg, user_arg, sizeof(arg)))
122                 return -EFAULT;
123
124         path = strndup_user((const char __user *)(unsigned long) arg.dev, PATH_MAX);
125         if (!path)
126                 return -ENOMEM;
127
128         ret = bch_dev_add(c, path);
129         kfree(path);
130
131         return ret;
132 }
133
134 /* returns with ref on ca->ref */
135 static struct cache *bch_device_lookup(struct cache_set *c,
136                                        const char __user *dev)
137 {
138         struct block_device *bdev;
139         struct cache *ca;
140         char *path;
141         unsigned i;
142
143         path = strndup_user(dev, PATH_MAX);
144         if (!path)
145                 return ERR_PTR(-ENOMEM);
146
147         bdev = lookup_bdev(strim(path));
148         kfree(path);
149         if (IS_ERR(bdev))
150                 return ERR_CAST(bdev);
151
152         for_each_cache(ca, c, i)
153                 if (ca->disk_sb.bdev == bdev)
154                         goto found;
155
156         ca = NULL;
157 found:
158         bdput(bdev);
159         return ca;
160 }
161
162 static long bch_ioctl_disk_remove(struct cache_set *c,
163                                   struct bch_ioctl_disk_remove __user *user_arg)
164 {
165         struct bch_ioctl_disk_remove arg;
166         struct cache *ca;
167         int ret;
168
169         if (copy_from_user(&arg, user_arg, sizeof(arg)))
170                 return -EFAULT;
171
172         ca = bch_device_lookup(c, (const char __user *)(unsigned long) arg.dev);
173         if (IS_ERR(ca))
174                 return PTR_ERR(ca);
175
176         ret = bch_dev_remove(ca, arg.flags & BCH_FORCE_IF_DATA_MISSING)
177                 ? 0 : -EBUSY;
178
179         percpu_ref_put(&ca->ref);
180         return ret;
181 }
182
183 static long bch_ioctl_disk_fail(struct cache_set *c,
184                                 struct bch_ioctl_disk_fail __user *user_arg)
185 {
186         struct bch_ioctl_disk_fail arg;
187         struct cache *ca;
188         int ret;
189
190         if (copy_from_user(&arg, user_arg, sizeof(arg)))
191                 return -EFAULT;
192
193         ca = bch_device_lookup(c, (const char __user *)(unsigned long) arg.dev);
194         if (IS_ERR(ca))
195                 return PTR_ERR(ca);
196
197         /* XXX: failed not actually implemented yet */
198         ret = bch_dev_remove(ca, true);
199
200         percpu_ref_put(&ca->ref);
201         return ret;
202 }
203
204 static struct bch_member *bch_uuid_lookup(struct cache_set *c, uuid_le uuid)
205 {
206         struct bch_sb_field_members *mi = bch_sb_get_members(c->disk_sb);
207         unsigned i;
208
209         lockdep_assert_held(&c->sb_lock);
210
211         for (i = 0; i < c->disk_sb->nr_devices; i++)
212                 if (!memcmp(&mi->members[i].uuid, &uuid, sizeof(uuid)))
213                         return &mi->members[i];
214
215         return NULL;
216 }
217
218 static long bch_ioctl_disk_remove_by_uuid(struct cache_set *c,
219                         struct bch_ioctl_disk_remove_by_uuid __user *user_arg)
220 {
221         struct bch_ioctl_disk_fail_by_uuid arg;
222         struct bch_member *m;
223         int ret = -ENOENT;
224
225         if (copy_from_user(&arg, user_arg, sizeof(arg)))
226                 return -EFAULT;
227
228         mutex_lock(&c->sb_lock);
229         if ((m = bch_uuid_lookup(c, arg.dev))) {
230                 /* XXX: */
231                 SET_BCH_MEMBER_STATE(m, BCH_MEMBER_STATE_FAILED);
232                 bch_write_super(c);
233                 ret = 0;
234         }
235         mutex_unlock(&c->sb_lock);
236
237         return ret;
238 }
239
240 static long bch_ioctl_disk_fail_by_uuid(struct cache_set *c,
241                         struct bch_ioctl_disk_fail_by_uuid __user *user_arg)
242 {
243         struct bch_ioctl_disk_fail_by_uuid arg;
244         struct bch_member *m;
245         int ret = -ENOENT;
246
247         if (copy_from_user(&arg, user_arg, sizeof(arg)))
248                 return -EFAULT;
249
250         mutex_lock(&c->sb_lock);
251         if ((m = bch_uuid_lookup(c, arg.dev))) {
252                 SET_BCH_MEMBER_STATE(m, BCH_MEMBER_STATE_FAILED);
253                 bch_write_super(c);
254                 ret = 0;
255         }
256         mutex_unlock(&c->sb_lock);
257
258         return ret;
259 }
260
261 static long bch_ioctl_query_uuid(struct cache_set *c,
262                         struct bch_ioctl_query_uuid __user *user_arg)
263 {
264         return copy_to_user(&user_arg->uuid,
265                             &c->sb.user_uuid,
266                             sizeof(c->sb.user_uuid));
267 }
268
269 long bch_fs_ioctl(struct cache_set *c, unsigned cmd, void __user *arg)
270 {
271         /* ioctls that don't require admin cap: */
272         switch (cmd) {
273         case BCH_IOCTL_QUERY_UUID:
274                 return bch_ioctl_query_uuid(c, arg);
275         }
276
277         if (!capable(CAP_SYS_ADMIN))
278                 return -EPERM;
279
280         /* ioctls that do require admin cap: */
281         switch (cmd) {
282         case BCH_IOCTL_RUN:
283                 return -ENOTTY;
284         case BCH_IOCTL_STOP:
285                 return bch_ioctl_stop(c);
286
287         case BCH_IOCTL_DISK_ADD:
288                 return bch_ioctl_disk_add(c, arg);
289         case BCH_IOCTL_DISK_REMOVE:
290                 return bch_ioctl_disk_remove(c, arg);
291         case BCH_IOCTL_DISK_FAIL:
292                 return bch_ioctl_disk_fail(c, arg);
293
294         case BCH_IOCTL_DISK_REMOVE_BY_UUID:
295                 return bch_ioctl_disk_remove_by_uuid(c, arg);
296         case BCH_IOCTL_DISK_FAIL_BY_UUID:
297                 return bch_ioctl_disk_fail_by_uuid(c, arg);
298
299         default:
300                 return -ENOTTY;
301         }
302 }
303
304 static long bch_chardev_ioctl(struct file *filp, unsigned cmd, unsigned long v)
305 {
306         struct cache_set *c = filp->private_data;
307         void __user *arg = (void __user *) v;
308
309         return c
310                 ? bch_fs_ioctl(c, cmd, arg)
311                 : bch_global_ioctl(cmd, arg);
312 }
313
314 static const struct file_operations bch_chardev_fops = {
315         .owner          = THIS_MODULE,
316         .unlocked_ioctl = bch_chardev_ioctl,
317         .open           = nonseekable_open,
318 };
319
320 static int bch_chardev_major;
321 static struct class *bch_chardev_class;
322 static struct device *bch_chardev;
323 static DEFINE_IDR(bch_chardev_minor);
324
325 void bch_fs_chardev_exit(struct cache_set *c)
326 {
327         if (!IS_ERR_OR_NULL(c->chardev))
328                 device_unregister(c->chardev);
329         if (c->minor >= 0)
330                 idr_remove(&bch_chardev_minor, c->minor);
331 }
332
333 int bch_fs_chardev_init(struct cache_set *c)
334 {
335         c->minor = idr_alloc(&bch_chardev_minor, c, 0, 0, GFP_KERNEL);
336         if (c->minor < 0)
337                 return c->minor;
338
339         c->chardev = device_create(bch_chardev_class, NULL,
340                                    MKDEV(bch_chardev_major, c->minor), NULL,
341                                    "bcache%u-ctl", c->minor);
342         if (IS_ERR(c->chardev))
343                 return PTR_ERR(c->chardev);
344
345         return 0;
346 }
347
348 void bch_chardev_exit(void)
349 {
350         if (!IS_ERR_OR_NULL(bch_chardev_class))
351                 device_destroy(bch_chardev_class,
352                                MKDEV(bch_chardev_major, 0));
353         if (!IS_ERR_OR_NULL(bch_chardev_class))
354                 class_destroy(bch_chardev_class);
355         if (bch_chardev_major > 0)
356                 unregister_chrdev(bch_chardev_major, "bcache");
357
358 }
359
360 int __init bch_chardev_init(void)
361 {
362         bch_chardev_major = register_chrdev(0, "bcache-ctl", &bch_chardev_fops);
363         if (bch_chardev_major < 0)
364                 return bch_chardev_major;
365
366         bch_chardev_class = class_create(THIS_MODULE, "bcache");
367         if (IS_ERR(bch_chardev_class))
368                 return PTR_ERR(bch_chardev_class);
369
370         bch_chardev = device_create(bch_chardev_class, NULL,
371                                     MKDEV(bch_chardev_major, 255),
372                                     NULL, "bcache-ctl");
373         if (IS_ERR(bch_chardev))
374                 return PTR_ERR(bch_chardev);
375
376         return 0;
377 }