2a20bdaf6142143f2e6376152762b7287f4f606d
[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->type->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 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 /* Caller must hold parent unreg_sem read or write lock */
59 static void mdev_device_remove_common(struct mdev_device *mdev)
60 {
61         struct mdev_parent *parent = mdev->type->parent;
62         int ret;
63
64         mdev_remove_sysfs_files(mdev);
65         device_del(&mdev->dev);
66         lockdep_assert_held(&parent->unreg_sem);
67         ret = parent->ops->remove(mdev);
68         if (ret)
69                 dev_err(&mdev->dev, "Remove failed: err=%d\n", ret);
70
71         /* Balances with device_initialize() */
72         put_device(&mdev->dev);
73 }
74
75 static int mdev_device_remove_cb(struct device *dev, void *data)
76 {
77         struct mdev_device *mdev = mdev_from_dev(dev);
78
79         if (mdev)
80                 mdev_device_remove_common(mdev);
81         return 0;
82 }
83
84 /*
85  * mdev_register_device : Register a device
86  * @dev: device structure representing parent device.
87  * @ops: Parent device operation structure to be registered.
88  *
89  * Add device to list of registered parent devices.
90  * Returns a negative value on error, otherwise 0.
91  */
92 int mdev_register_device(struct device *dev, const struct mdev_parent_ops *ops)
93 {
94         int ret;
95         struct mdev_parent *parent;
96         char *env_string = "MDEV_STATE=registered";
97         char *envp[] = { env_string, NULL };
98
99         /* check for mandatory ops */
100         if (!ops || !ops->create || !ops->remove || !ops->supported_type_groups)
101                 return -EINVAL;
102
103         dev = get_device(dev);
104         if (!dev)
105                 return -EINVAL;
106
107         /* Not mandatory, but its absence could be a problem */
108         if (!ops->request)
109                 dev_info(dev, "Driver cannot be asked to release device\n");
110
111         mutex_lock(&parent_list_lock);
112
113         /* Check for duplicate */
114         parent = __find_parent_device(dev);
115         if (parent) {
116                 parent = NULL;
117                 ret = -EEXIST;
118                 goto add_dev_err;
119         }
120
121         parent = kzalloc(sizeof(*parent), GFP_KERNEL);
122         if (!parent) {
123                 ret = -ENOMEM;
124                 goto add_dev_err;
125         }
126
127         kref_init(&parent->ref);
128         init_rwsem(&parent->unreg_sem);
129
130         parent->dev = dev;
131         parent->ops = ops;
132
133         if (!mdev_bus_compat_class) {
134                 mdev_bus_compat_class = class_compat_register("mdev_bus");
135                 if (!mdev_bus_compat_class) {
136                         ret = -ENOMEM;
137                         goto add_dev_err;
138                 }
139         }
140
141         ret = parent_create_sysfs_files(parent);
142         if (ret)
143                 goto add_dev_err;
144
145         ret = class_compat_create_link(mdev_bus_compat_class, dev, NULL);
146         if (ret)
147                 dev_warn(dev, "Failed to create compatibility class link\n");
148
149         list_add(&parent->next, &parent_list);
150         mutex_unlock(&parent_list_lock);
151
152         dev_info(dev, "MDEV: Registered\n");
153         kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
154
155         return 0;
156
157 add_dev_err:
158         mutex_unlock(&parent_list_lock);
159         if (parent)
160                 mdev_put_parent(parent);
161         else
162                 put_device(dev);
163         return ret;
164 }
165 EXPORT_SYMBOL(mdev_register_device);
166
167 /*
168  * mdev_unregister_device : Unregister a parent device
169  * @dev: device structure representing parent device.
170  *
171  * Remove device from list of registered parent devices. Give a chance to free
172  * existing mediated devices for given device.
173  */
174
175 void mdev_unregister_device(struct device *dev)
176 {
177         struct mdev_parent *parent;
178         char *env_string = "MDEV_STATE=unregistered";
179         char *envp[] = { env_string, NULL };
180
181         mutex_lock(&parent_list_lock);
182         parent = __find_parent_device(dev);
183
184         if (!parent) {
185                 mutex_unlock(&parent_list_lock);
186                 return;
187         }
188         dev_info(dev, "MDEV: Unregistering\n");
189
190         list_del(&parent->next);
191         mutex_unlock(&parent_list_lock);
192
193         down_write(&parent->unreg_sem);
194
195         class_compat_remove_link(mdev_bus_compat_class, dev, NULL);
196
197         device_for_each_child(dev, NULL, mdev_device_remove_cb);
198
199         parent_remove_sysfs_files(parent);
200         up_write(&parent->unreg_sem);
201
202         mdev_put_parent(parent);
203
204         /* We still have the caller's reference to use for the uevent */
205         kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
206 }
207 EXPORT_SYMBOL(mdev_unregister_device);
208
209 static void mdev_device_release(struct device *dev)
210 {
211         struct mdev_device *mdev = to_mdev_device(dev);
212
213         /* Pairs with the get in mdev_device_create() */
214         kobject_put(&mdev->type->kobj);
215
216         mutex_lock(&mdev_list_lock);
217         list_del(&mdev->next);
218         mutex_unlock(&mdev_list_lock);
219
220         dev_dbg(&mdev->dev, "MDEV: destroying\n");
221         kfree(mdev);
222 }
223
224 int mdev_device_create(struct mdev_type *type, const guid_t *uuid)
225 {
226         int ret;
227         struct mdev_device *mdev, *tmp;
228         struct mdev_parent *parent = type->parent;
229
230         mutex_lock(&mdev_list_lock);
231
232         /* Check for duplicate */
233         list_for_each_entry(tmp, &mdev_list, next) {
234                 if (guid_equal(&tmp->uuid, uuid)) {
235                         mutex_unlock(&mdev_list_lock);
236                         return -EEXIST;
237                 }
238         }
239
240         mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
241         if (!mdev) {
242                 mutex_unlock(&mdev_list_lock);
243                 return -ENOMEM;
244         }
245
246         device_initialize(&mdev->dev);
247         mdev->dev.parent  = parent->dev;
248         mdev->dev.bus = &mdev_bus_type;
249         mdev->dev.release = mdev_device_release;
250         mdev->dev.groups = parent->ops->mdev_attr_groups;
251         mdev->type = type;
252         /* Pairs with the put in mdev_device_release() */
253         kobject_get(&type->kobj);
254
255         guid_copy(&mdev->uuid, uuid);
256         list_add(&mdev->next, &mdev_list);
257         mutex_unlock(&mdev_list_lock);
258
259         ret = dev_set_name(&mdev->dev, "%pUl", uuid);
260         if (ret)
261                 goto out_put_device;
262
263         /* Check if parent unregistration has started */
264         if (!down_read_trylock(&parent->unreg_sem)) {
265                 ret = -ENODEV;
266                 goto out_put_device;
267         }
268
269         ret = parent->ops->create(&type->kobj, mdev);
270         if (ret)
271                 goto out_unlock;
272
273         ret = device_add(&mdev->dev);
274         if (ret)
275                 goto out_remove;
276
277         ret = mdev_create_sysfs_files(mdev);
278         if (ret)
279                 goto out_del;
280
281         mdev->active = true;
282         dev_dbg(&mdev->dev, "MDEV: created\n");
283         up_read(&parent->unreg_sem);
284
285         return 0;
286
287 out_del:
288         device_del(&mdev->dev);
289 out_remove:
290         parent->ops->remove(mdev);
291 out_unlock:
292         up_read(&parent->unreg_sem);
293 out_put_device:
294         put_device(&mdev->dev);
295         return ret;
296 }
297
298 int mdev_device_remove(struct mdev_device *mdev)
299 {
300         struct mdev_device *tmp;
301         struct mdev_parent *parent = mdev->type->parent;
302
303         mutex_lock(&mdev_list_lock);
304         list_for_each_entry(tmp, &mdev_list, next) {
305                 if (tmp == mdev)
306                         break;
307         }
308
309         if (tmp != mdev) {
310                 mutex_unlock(&mdev_list_lock);
311                 return -ENODEV;
312         }
313
314         if (!mdev->active) {
315                 mutex_unlock(&mdev_list_lock);
316                 return -EAGAIN;
317         }
318
319         mdev->active = false;
320         mutex_unlock(&mdev_list_lock);
321
322         /* Check if parent unregistration has started */
323         if (!down_read_trylock(&parent->unreg_sem))
324                 return -ENODEV;
325
326         mdev_device_remove_common(mdev);
327         up_read(&parent->unreg_sem);
328         return 0;
329 }
330
331 static int __init mdev_init(void)
332 {
333         return mdev_bus_register();
334 }
335
336 static void __exit mdev_exit(void)
337 {
338         if (mdev_bus_compat_class)
339                 class_compat_unregister(mdev_bus_compat_class);
340
341         mdev_bus_unregister();
342 }
343
344 module_init(mdev_init)
345 module_exit(mdev_exit)
346
347 MODULE_VERSION(DRIVER_VERSION);
348 MODULE_LICENSE("GPL v2");
349 MODULE_AUTHOR(DRIVER_AUTHOR);
350 MODULE_DESCRIPTION(DRIVER_DESC);
351 MODULE_SOFTDEP("post: vfio_mdev");