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