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