vfio/mdev: Add missing typesafety around mdev_device
[linux-2.6-microblaze.git] / drivers / vfio / mdev / mdev_core.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Mediated device Core Driver
4  *
5  * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
6  *     Author: Neo Jia <cjia@nvidia.com>
7  *             Kirti Wankhede <kwankhede@nvidia.com>
8  */
9
10 #include <linux/module.h>
11 #include <linux/device.h>
12 #include <linux/slab.h>
13 #include <linux/uuid.h>
14 #include <linux/sysfs.h>
15 #include <linux/mdev.h>
16
17 #include "mdev_private.h"
18
19 #define DRIVER_VERSION          "0.1"
20 #define DRIVER_AUTHOR           "NVIDIA Corporation"
21 #define DRIVER_DESC             "Mediated device Core Driver"
22
23 static LIST_HEAD(parent_list);
24 static DEFINE_MUTEX(parent_list_lock);
25 static struct class_compat *mdev_bus_compat_class;
26
27 static LIST_HEAD(mdev_list);
28 static DEFINE_MUTEX(mdev_list_lock);
29
30 struct device *mdev_parent_dev(struct mdev_device *mdev)
31 {
32         return mdev->parent->dev;
33 }
34 EXPORT_SYMBOL(mdev_parent_dev);
35
36 /* Should be called holding parent_list_lock */
37 static struct mdev_parent *__find_parent_device(struct device *dev)
38 {
39         struct mdev_parent *parent;
40
41         list_for_each_entry(parent, &parent_list, next) {
42                 if (parent->dev == dev)
43                         return parent;
44         }
45         return NULL;
46 }
47
48 static void mdev_release_parent(struct kref *kref)
49 {
50         struct mdev_parent *parent = container_of(kref, struct mdev_parent,
51                                                   ref);
52         struct device *dev = parent->dev;
53
54         kfree(parent);
55         put_device(dev);
56 }
57
58 static struct mdev_parent *mdev_get_parent(struct mdev_parent *parent)
59 {
60         if (parent)
61                 kref_get(&parent->ref);
62
63         return parent;
64 }
65
66 static void mdev_put_parent(struct mdev_parent *parent)
67 {
68         if (parent)
69                 kref_put(&parent->ref, mdev_release_parent);
70 }
71
72 /* Caller must hold parent unreg_sem read or write lock */
73 static void mdev_device_remove_common(struct mdev_device *mdev)
74 {
75         struct mdev_parent *parent;
76         struct mdev_type *type;
77         int ret;
78
79         type = to_mdev_type(mdev->type_kobj);
80         mdev_remove_sysfs_files(mdev, type);
81         device_del(&mdev->dev);
82         parent = mdev->parent;
83         lockdep_assert_held(&parent->unreg_sem);
84         ret = parent->ops->remove(mdev);
85         if (ret)
86                 dev_err(&mdev->dev, "Remove failed: err=%d\n", ret);
87
88         /* Balances with device_initialize() */
89         put_device(&mdev->dev);
90         mdev_put_parent(parent);
91 }
92
93 static int mdev_device_remove_cb(struct device *dev, void *data)
94 {
95         struct mdev_device *mdev = mdev_from_dev(dev);
96
97         if (mdev)
98                 mdev_device_remove_common(mdev);
99         return 0;
100 }
101
102 /*
103  * mdev_register_device : Register a device
104  * @dev: device structure representing parent device.
105  * @ops: Parent device operation structure to be registered.
106  *
107  * Add device to list of registered parent devices.
108  * Returns a negative value on error, otherwise 0.
109  */
110 int mdev_register_device(struct device *dev, const struct mdev_parent_ops *ops)
111 {
112         int ret;
113         struct mdev_parent *parent;
114         char *env_string = "MDEV_STATE=registered";
115         char *envp[] = { env_string, NULL };
116
117         /* check for mandatory ops */
118         if (!ops || !ops->create || !ops->remove || !ops->supported_type_groups)
119                 return -EINVAL;
120
121         dev = get_device(dev);
122         if (!dev)
123                 return -EINVAL;
124
125         /* Not mandatory, but its absence could be a problem */
126         if (!ops->request)
127                 dev_info(dev, "Driver cannot be asked to release device\n");
128
129         mutex_lock(&parent_list_lock);
130
131         /* Check for duplicate */
132         parent = __find_parent_device(dev);
133         if (parent) {
134                 parent = NULL;
135                 ret = -EEXIST;
136                 goto add_dev_err;
137         }
138
139         parent = kzalloc(sizeof(*parent), GFP_KERNEL);
140         if (!parent) {
141                 ret = -ENOMEM;
142                 goto add_dev_err;
143         }
144
145         kref_init(&parent->ref);
146         init_rwsem(&parent->unreg_sem);
147
148         parent->dev = dev;
149         parent->ops = ops;
150
151         if (!mdev_bus_compat_class) {
152                 mdev_bus_compat_class = class_compat_register("mdev_bus");
153                 if (!mdev_bus_compat_class) {
154                         ret = -ENOMEM;
155                         goto add_dev_err;
156                 }
157         }
158
159         ret = parent_create_sysfs_files(parent);
160         if (ret)
161                 goto add_dev_err;
162
163         ret = class_compat_create_link(mdev_bus_compat_class, dev, NULL);
164         if (ret)
165                 dev_warn(dev, "Failed to create compatibility class link\n");
166
167         list_add(&parent->next, &parent_list);
168         mutex_unlock(&parent_list_lock);
169
170         dev_info(dev, "MDEV: Registered\n");
171         kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
172
173         return 0;
174
175 add_dev_err:
176         mutex_unlock(&parent_list_lock);
177         if (parent)
178                 mdev_put_parent(parent);
179         else
180                 put_device(dev);
181         return ret;
182 }
183 EXPORT_SYMBOL(mdev_register_device);
184
185 /*
186  * mdev_unregister_device : Unregister a parent device
187  * @dev: device structure representing parent device.
188  *
189  * Remove device from list of registered parent devices. Give a chance to free
190  * existing mediated devices for given device.
191  */
192
193 void mdev_unregister_device(struct device *dev)
194 {
195         struct mdev_parent *parent;
196         char *env_string = "MDEV_STATE=unregistered";
197         char *envp[] = { env_string, NULL };
198
199         mutex_lock(&parent_list_lock);
200         parent = __find_parent_device(dev);
201
202         if (!parent) {
203                 mutex_unlock(&parent_list_lock);
204                 return;
205         }
206         dev_info(dev, "MDEV: Unregistering\n");
207
208         list_del(&parent->next);
209         mutex_unlock(&parent_list_lock);
210
211         down_write(&parent->unreg_sem);
212
213         class_compat_remove_link(mdev_bus_compat_class, dev, NULL);
214
215         device_for_each_child(dev, NULL, mdev_device_remove_cb);
216
217         parent_remove_sysfs_files(parent);
218         up_write(&parent->unreg_sem);
219
220         mdev_put_parent(parent);
221
222         /* We still have the caller's reference to use for the uevent */
223         kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
224 }
225 EXPORT_SYMBOL(mdev_unregister_device);
226
227 static void mdev_device_free(struct mdev_device *mdev)
228 {
229         mutex_lock(&mdev_list_lock);
230         list_del(&mdev->next);
231         mutex_unlock(&mdev_list_lock);
232
233         dev_dbg(&mdev->dev, "MDEV: destroying\n");
234         kfree(mdev);
235 }
236
237 static void mdev_device_release(struct device *dev)
238 {
239         struct mdev_device *mdev = to_mdev_device(dev);
240
241         mdev_device_free(mdev);
242 }
243
244 int mdev_device_create(struct kobject *kobj,
245                        struct device *dev, const guid_t *uuid)
246 {
247         int ret;
248         struct mdev_device *mdev, *tmp;
249         struct mdev_parent *parent;
250         struct mdev_type *type = to_mdev_type(kobj);
251
252         parent = mdev_get_parent(type->parent);
253         if (!parent)
254                 return -EINVAL;
255
256         mutex_lock(&mdev_list_lock);
257
258         /* Check for duplicate */
259         list_for_each_entry(tmp, &mdev_list, next) {
260                 if (guid_equal(&tmp->uuid, uuid)) {
261                         mutex_unlock(&mdev_list_lock);
262                         ret = -EEXIST;
263                         goto mdev_fail;
264                 }
265         }
266
267         mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
268         if (!mdev) {
269                 mutex_unlock(&mdev_list_lock);
270                 ret = -ENOMEM;
271                 goto mdev_fail;
272         }
273
274         guid_copy(&mdev->uuid, uuid);
275         list_add(&mdev->next, &mdev_list);
276         mutex_unlock(&mdev_list_lock);
277
278         mdev->parent = parent;
279
280         /* Check if parent unregistration has started */
281         if (!down_read_trylock(&parent->unreg_sem)) {
282                 mdev_device_free(mdev);
283                 ret = -ENODEV;
284                 goto mdev_fail;
285         }
286
287         device_initialize(&mdev->dev);
288         mdev->dev.parent  = dev;
289         mdev->dev.bus     = &mdev_bus_type;
290         mdev->dev.release = mdev_device_release;
291         dev_set_name(&mdev->dev, "%pUl", uuid);
292         mdev->dev.groups = parent->ops->mdev_attr_groups;
293         mdev->type_kobj = kobj;
294
295         ret = parent->ops->create(kobj, mdev);
296         if (ret)
297                 goto ops_create_fail;
298
299         ret = device_add(&mdev->dev);
300         if (ret)
301                 goto add_fail;
302
303         ret = mdev_create_sysfs_files(mdev, type);
304         if (ret)
305                 goto sysfs_fail;
306
307         mdev->active = true;
308         dev_dbg(&mdev->dev, "MDEV: created\n");
309         up_read(&parent->unreg_sem);
310
311         return 0;
312
313 sysfs_fail:
314         device_del(&mdev->dev);
315 add_fail:
316         parent->ops->remove(mdev);
317 ops_create_fail:
318         up_read(&parent->unreg_sem);
319         put_device(&mdev->dev);
320 mdev_fail:
321         mdev_put_parent(parent);
322         return ret;
323 }
324
325 int mdev_device_remove(struct mdev_device *mdev)
326 {
327         struct mdev_device *tmp;
328         struct mdev_parent *parent;
329
330         mutex_lock(&mdev_list_lock);
331         list_for_each_entry(tmp, &mdev_list, next) {
332                 if (tmp == mdev)
333                         break;
334         }
335
336         if (tmp != mdev) {
337                 mutex_unlock(&mdev_list_lock);
338                 return -ENODEV;
339         }
340
341         if (!mdev->active) {
342                 mutex_unlock(&mdev_list_lock);
343                 return -EAGAIN;
344         }
345
346         mdev->active = false;
347         mutex_unlock(&mdev_list_lock);
348
349         parent = mdev->parent;
350         /* Check if parent unregistration has started */
351         if (!down_read_trylock(&parent->unreg_sem))
352                 return -ENODEV;
353
354         mdev_device_remove_common(mdev);
355         up_read(&parent->unreg_sem);
356         return 0;
357 }
358
359 static int __init mdev_init(void)
360 {
361         return mdev_bus_register();
362 }
363
364 static void __exit mdev_exit(void)
365 {
366         if (mdev_bus_compat_class)
367                 class_compat_unregister(mdev_bus_compat_class);
368
369         mdev_bus_unregister();
370 }
371
372 module_init(mdev_init)
373 module_exit(mdev_exit)
374
375 MODULE_VERSION(DRIVER_VERSION);
376 MODULE_LICENSE("GPL v2");
377 MODULE_AUTHOR(DRIVER_AUTHOR);
378 MODULE_DESCRIPTION(DRIVER_DESC);
379 MODULE_SOFTDEP("post: vfio_mdev");