Linux Audio

Check our new training course

Loading...
v4.17
 
  1/*
  2 * Mediated device Core Driver
  3 *
  4 * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
  5 *     Author: Neo Jia <cjia@nvidia.com>
  6 *             Kirti Wankhede <kwankhede@nvidia.com>
  7 *
  8 * This program is free software; you can redistribute it and/or modify
  9 * it under the terms of the GNU General Public License version 2 as
 10 * published by the Free Software Foundation.
 11 */
 12
 13#include <linux/module.h>
 14#include <linux/device.h>
 15#include <linux/slab.h>
 16#include <linux/uuid.h>
 17#include <linux/sysfs.h>
 18#include <linux/mdev.h>
 19
 20#include "mdev_private.h"
 21
 22#define DRIVER_VERSION		"0.1"
 23#define DRIVER_AUTHOR		"NVIDIA Corporation"
 24#define DRIVER_DESC		"Mediated device Core Driver"
 25
 26static LIST_HEAD(parent_list);
 27static DEFINE_MUTEX(parent_list_lock);
 28static struct class_compat *mdev_bus_compat_class;
 29
 30static LIST_HEAD(mdev_list);
 31static DEFINE_MUTEX(mdev_list_lock);
 32
 33struct device *mdev_parent_dev(struct mdev_device *mdev)
 34{
 35	return mdev->parent->dev;
 36}
 37EXPORT_SYMBOL(mdev_parent_dev);
 38
 39void *mdev_get_drvdata(struct mdev_device *mdev)
 40{
 41	return mdev->driver_data;
 42}
 43EXPORT_SYMBOL(mdev_get_drvdata);
 44
 45void mdev_set_drvdata(struct mdev_device *mdev, void *data)
 46{
 47	mdev->driver_data = data;
 48}
 49EXPORT_SYMBOL(mdev_set_drvdata);
 50
 51struct device *mdev_dev(struct mdev_device *mdev)
 52{
 53	return &mdev->dev;
 54}
 55EXPORT_SYMBOL(mdev_dev);
 56
 57struct mdev_device *mdev_from_dev(struct device *dev)
 58{
 59	return dev_is_mdev(dev) ? to_mdev_device(dev) : NULL;
 60}
 61EXPORT_SYMBOL(mdev_from_dev);
 62
 63uuid_le mdev_uuid(struct mdev_device *mdev)
 64{
 65	return mdev->uuid;
 66}
 67EXPORT_SYMBOL(mdev_uuid);
 68
 69static int _find_mdev_device(struct device *dev, void *data)
 70{
 71	struct mdev_device *mdev;
 72
 73	if (!dev_is_mdev(dev))
 74		return 0;
 75
 76	mdev = to_mdev_device(dev);
 77
 78	if (uuid_le_cmp(mdev->uuid, *(uuid_le *)data) == 0)
 79		return 1;
 80
 81	return 0;
 82}
 83
 84static bool mdev_device_exist(struct mdev_parent *parent, uuid_le uuid)
 85{
 86	struct device *dev;
 87
 88	dev = device_find_child(parent->dev, &uuid, _find_mdev_device);
 89	if (dev) {
 90		put_device(dev);
 91		return true;
 92	}
 93
 94	return false;
 95}
 96
 97/* Should be called holding parent_list_lock */
 98static struct mdev_parent *__find_parent_device(struct device *dev)
 99{
100	struct mdev_parent *parent;
101
102	list_for_each_entry(parent, &parent_list, next) {
103		if (parent->dev == dev)
104			return parent;
105	}
106	return NULL;
107}
108
109static void mdev_release_parent(struct kref *kref)
110{
111	struct mdev_parent *parent = container_of(kref, struct mdev_parent,
112						  ref);
113	struct device *dev = parent->dev;
114
115	kfree(parent);
116	put_device(dev);
117}
118
119static
120inline struct mdev_parent *mdev_get_parent(struct mdev_parent *parent)
121{
122	if (parent)
123		kref_get(&parent->ref);
124
125	return parent;
126}
127
128static inline void mdev_put_parent(struct mdev_parent *parent)
129{
130	if (parent)
131		kref_put(&parent->ref, mdev_release_parent);
132}
133
134static int mdev_device_create_ops(struct kobject *kobj,
135				  struct mdev_device *mdev)
136{
137	struct mdev_parent *parent = mdev->parent;
138	int ret;
139
140	ret = parent->ops->create(kobj, mdev);
141	if (ret)
142		return ret;
143
144	ret = sysfs_create_groups(&mdev->dev.kobj,
145				  parent->ops->mdev_attr_groups);
146	if (ret)
147		parent->ops->remove(mdev);
148
149	return ret;
150}
151
152/*
153 * mdev_device_remove_ops gets called from sysfs's 'remove' and when parent
154 * device is being unregistered from mdev device framework.
155 * - 'force_remove' is set to 'false' when called from sysfs's 'remove' which
156 *   indicates that if the mdev device is active, used by VMM or userspace
157 *   application, vendor driver could return error then don't remove the device.
158 * - 'force_remove' is set to 'true' when called from mdev_unregister_device()
159 *   which indicate that parent device is being removed from mdev device
160 *   framework so remove mdev device forcefully.
161 */
162static int mdev_device_remove_ops(struct mdev_device *mdev, bool force_remove)
163{
164	struct mdev_parent *parent = mdev->parent;
165	int ret;
166
167	/*
168	 * Vendor driver can return error if VMM or userspace application is
169	 * using this mdev device.
170	 */
171	ret = parent->ops->remove(mdev);
172	if (ret && !force_remove)
173		return -EBUSY;
174
175	sysfs_remove_groups(&mdev->dev.kobj, parent->ops->mdev_attr_groups);
176	return 0;
177}
178
179static int mdev_device_remove_cb(struct device *dev, void *data)
180{
181	if (!dev_is_mdev(dev))
182		return 0;
183
184	return mdev_device_remove(dev, data ? *(bool *)data : true);
185}
186
187/*
188 * mdev_register_device : Register a device
 
189 * @dev: device structure representing parent device.
190 * @ops: Parent device operation structure to be registered.
 
 
 
 
 
 
191 *
192 * Add device to list of registered parent devices.
193 * Returns a negative value on error, otherwise 0.
194 */
195int mdev_register_device(struct device *dev, const struct mdev_parent_ops *ops)
 
 
196{
 
 
197	int ret;
198	struct mdev_parent *parent;
199
200	/* check for mandatory ops */
201	if (!ops || !ops->create || !ops->remove || !ops->supported_type_groups)
202		return -EINVAL;
203
204	dev = get_device(dev);
205	if (!dev)
206		return -EINVAL;
207
208	mutex_lock(&parent_list_lock);
209
210	/* Check for duplicate */
211	parent = __find_parent_device(dev);
212	if (parent) {
213		ret = -EEXIST;
214		goto add_dev_err;
215	}
216
217	parent = kzalloc(sizeof(*parent), GFP_KERNEL);
218	if (!parent) {
219		ret = -ENOMEM;
220		goto add_dev_err;
221	}
222
223	kref_init(&parent->ref);
224	mutex_init(&parent->lock);
225
 
 
226	parent->dev = dev;
227	parent->ops = ops;
 
 
 
228
229	if (!mdev_bus_compat_class) {
230		mdev_bus_compat_class = class_compat_register("mdev_bus");
231		if (!mdev_bus_compat_class) {
232			ret = -ENOMEM;
233			goto add_dev_err;
234		}
235	}
236
237	ret = parent_create_sysfs_files(parent);
238	if (ret)
239		goto add_dev_err;
240
241	ret = class_compat_create_link(mdev_bus_compat_class, dev, NULL);
242	if (ret)
243		dev_warn(dev, "Failed to create compatibility class link\n");
244
245	list_add(&parent->next, &parent_list);
246	mutex_unlock(&parent_list_lock);
247
248	dev_info(dev, "MDEV: Registered\n");
 
249	return 0;
250
251add_dev_err:
252	mutex_unlock(&parent_list_lock);
253	if (parent)
254		mdev_put_parent(parent);
255	else
256		put_device(dev);
257	return ret;
258}
259EXPORT_SYMBOL(mdev_register_device);
260
261/*
262 * mdev_unregister_device : Unregister a parent device
263 * @dev: device structure representing parent device.
264 *
265 * Remove device from list of registered parent devices. Give a chance to free
266 * existing mediated devices for given device.
267 */
268
269void mdev_unregister_device(struct device *dev)
270{
271	struct mdev_parent *parent;
272	bool force_remove = true;
273
274	mutex_lock(&parent_list_lock);
275	parent = __find_parent_device(dev);
276
277	if (!parent) {
278		mutex_unlock(&parent_list_lock);
279		return;
280	}
281	dev_info(dev, "MDEV: Unregistering\n");
282
283	list_del(&parent->next);
284	class_compat_remove_link(mdev_bus_compat_class, dev, NULL);
285
286	device_for_each_child(dev, (void *)&force_remove,
287			      mdev_device_remove_cb);
288
 
 
 
289	parent_remove_sysfs_files(parent);
 
290
291	mutex_unlock(&parent_list_lock);
292	mdev_put_parent(parent);
293}
294EXPORT_SYMBOL(mdev_unregister_device);
295
296static void mdev_device_release(struct device *dev)
297{
298	struct mdev_device *mdev = to_mdev_device(dev);
 
 
 
 
 
 
 
 
 
 
299
300	dev_dbg(&mdev->dev, "MDEV: destroying\n");
301	kfree(mdev);
302}
303
304int mdev_device_create(struct kobject *kobj, struct device *dev, uuid_le uuid)
305{
306	int ret;
307	struct mdev_device *mdev;
308	struct mdev_parent *parent;
309	struct mdev_type *type = to_mdev_type(kobj);
310
311	parent = mdev_get_parent(type->parent);
312	if (!parent)
313		return -EINVAL;
314
315	mutex_lock(&parent->lock);
316
317	/* Check for duplicate */
318	if (mdev_device_exist(parent, uuid)) {
319		ret = -EEXIST;
320		goto create_err;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321	}
322
323	mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
324	if (!mdev) {
325		ret = -ENOMEM;
326		goto create_err;
327	}
328
329	memcpy(&mdev->uuid, &uuid, sizeof(uuid_le));
330	mdev->parent = parent;
331	kref_init(&mdev->ref);
332
333	mdev->dev.parent  = dev;
334	mdev->dev.bus     = &mdev_bus_type;
335	mdev->dev.release = mdev_device_release;
336	dev_set_name(&mdev->dev, "%pUl", uuid.b);
 
 
 
337
338	ret = device_register(&mdev->dev);
339	if (ret) {
340		put_device(&mdev->dev);
341		goto create_err;
342	}
343
344	ret = mdev_device_create_ops(kobj, mdev);
345	if (ret)
346		goto create_failed;
347
348	ret = mdev_create_sysfs_files(&mdev->dev, type);
349	if (ret) {
350		mdev_device_remove_ops(mdev, true);
351		goto create_failed;
352	}
353
354	mdev->type_kobj = kobj;
355	dev_dbg(&mdev->dev, "MDEV: created\n");
 
356
357	mutex_unlock(&parent->lock);
 
 
358
359	mutex_lock(&mdev_list_lock);
360	list_add(&mdev->next, &mdev_list);
361	mutex_unlock(&mdev_list_lock);
362
363	return ret;
 
 
364
365create_failed:
366	device_unregister(&mdev->dev);
367
368create_err:
369	mutex_unlock(&parent->lock);
370	mdev_put_parent(parent);
 
 
 
371	return ret;
372}
373
374int mdev_device_remove(struct device *dev, bool force_remove)
375{
376	struct mdev_device *mdev, *tmp;
377	struct mdev_parent *parent;
378	struct mdev_type *type;
379	int ret;
380	bool found = false;
381
382	mdev = to_mdev_device(dev);
383
384	mutex_lock(&mdev_list_lock);
385	list_for_each_entry(tmp, &mdev_list, next) {
386		if (tmp == mdev) {
387			found = true;
388			break;
389		}
390	}
391
392	if (found)
393		list_del(&mdev->next);
394
395	mutex_unlock(&mdev_list_lock);
396
397	if (!found)
398		return -ENODEV;
 
399
400	type = to_mdev_type(mdev->type_kobj);
401	parent = mdev->parent;
402	mutex_lock(&parent->lock);
403
404	ret = mdev_device_remove_ops(mdev, force_remove);
405	if (ret) {
406		mutex_unlock(&parent->lock);
407
408		mutex_lock(&mdev_list_lock);
409		list_add(&mdev->next, &mdev_list);
410		mutex_unlock(&mdev_list_lock);
411
412		return ret;
413	}
414
415	mdev_remove_sysfs_files(dev, type);
416	device_unregister(dev);
417	mutex_unlock(&parent->lock);
418	mdev_put_parent(parent);
 
 
419
 
 
420	return 0;
421}
422
423static int __init mdev_init(void)
424{
425	return mdev_bus_register();
426}
427
428static void __exit mdev_exit(void)
429{
430	if (mdev_bus_compat_class)
431		class_compat_unregister(mdev_bus_compat_class);
432
433	mdev_bus_unregister();
434}
435
436module_init(mdev_init)
437module_exit(mdev_exit)
438
439MODULE_VERSION(DRIVER_VERSION);
440MODULE_LICENSE("GPL v2");
441MODULE_AUTHOR(DRIVER_AUTHOR);
442MODULE_DESCRIPTION(DRIVER_DESC);
443MODULE_SOFTDEP("post: vfio_mdev");
v6.2
  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/slab.h>
 
 12#include <linux/sysfs.h>
 13#include <linux/mdev.h>
 14
 15#include "mdev_private.h"
 16
 17#define DRIVER_VERSION		"0.1"
 18#define DRIVER_AUTHOR		"NVIDIA Corporation"
 19#define DRIVER_DESC		"Mediated device Core Driver"
 20
 
 
 21static struct class_compat *mdev_bus_compat_class;
 22
 23static LIST_HEAD(mdev_list);
 24static DEFINE_MUTEX(mdev_list_lock);
 25
 26/* Caller must hold parent unreg_sem read or write lock */
 27static void mdev_device_remove_common(struct mdev_device *mdev)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 28{
 29	struct mdev_parent *parent = mdev->type->parent;
 30
 31	mdev_remove_sysfs_files(mdev);
 32	device_del(&mdev->dev);
 33	lockdep_assert_held(&parent->unreg_sem);
 34	/* Balances with device_initialize() */
 35	put_device(&mdev->dev);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 36}
 37
 38static int mdev_device_remove_cb(struct device *dev, void *data)
 39{
 40	if (dev->bus == &mdev_bus_type)
 41		mdev_device_remove_common(to_mdev_device(dev));
 42	return 0;
 
 43}
 44
 45/*
 46 * mdev_register_parent: Register a device as parent for mdevs
 47 * @parent: parent structure registered
 48 * @dev: device structure representing parent device.
 49 * @mdev_driver: Device driver to bind to the newly created mdev
 50 * @types: Array of supported mdev types
 51 * @nr_types: Number of entries in @types
 52 *
 53 * Registers the @parent stucture as a parent for mdev types and thus mdev
 54 * devices.  The caller needs to hold a reference on @dev that must not be
 55 * released until after the call to mdev_unregister_parent().
 56 *
 
 57 * Returns a negative value on error, otherwise 0.
 58 */
 59int mdev_register_parent(struct mdev_parent *parent, struct device *dev,
 60		struct mdev_driver *mdev_driver, struct mdev_type **types,
 61		unsigned int nr_types)
 62{
 63	char *env_string = "MDEV_STATE=registered";
 64	char *envp[] = { env_string, NULL };
 65	int ret;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 66
 67	memset(parent, 0, sizeof(*parent));
 68	init_rwsem(&parent->unreg_sem);
 69	parent->dev = dev;
 70	parent->mdev_driver = mdev_driver;
 71	parent->types = types;
 72	parent->nr_types = nr_types;
 73	atomic_set(&parent->available_instances, mdev_driver->max_instances);
 74
 75	if (!mdev_bus_compat_class) {
 76		mdev_bus_compat_class = class_compat_register("mdev_bus");
 77		if (!mdev_bus_compat_class)
 78			return -ENOMEM;
 
 
 79	}
 80
 81	ret = parent_create_sysfs_files(parent);
 82	if (ret)
 83		return ret;
 84
 85	ret = class_compat_create_link(mdev_bus_compat_class, dev, NULL);
 86	if (ret)
 87		dev_warn(dev, "Failed to create compatibility class link\n");
 88
 
 
 
 89	dev_info(dev, "MDEV: Registered\n");
 90	kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
 91	return 0;
 
 
 
 
 
 
 
 
 92}
 93EXPORT_SYMBOL(mdev_register_parent);
 94
 95/*
 96 * mdev_unregister_parent : Unregister a parent device
 97 * @parent: parent structure to unregister
 
 
 
 98 */
 99void mdev_unregister_parent(struct mdev_parent *parent)
 
100{
101	char *env_string = "MDEV_STATE=unregistered";
102	char *envp[] = { env_string, NULL };
 
 
 
 
 
 
 
 
 
 
 
 
103
104	dev_info(parent->dev, "MDEV: Unregistering\n");
 
105
106	down_write(&parent->unreg_sem);
107	class_compat_remove_link(mdev_bus_compat_class, parent->dev, NULL);
108	device_for_each_child(parent->dev, NULL, mdev_device_remove_cb);
109	parent_remove_sysfs_files(parent);
110	up_write(&parent->unreg_sem);
111
112	kobject_uevent_env(&parent->dev->kobj, KOBJ_CHANGE, envp);
 
113}
114EXPORT_SYMBOL(mdev_unregister_parent);
115
116static void mdev_device_release(struct device *dev)
117{
118	struct mdev_device *mdev = to_mdev_device(dev);
119	struct mdev_parent *parent = mdev->type->parent;
120
121	mutex_lock(&mdev_list_lock);
122	list_del(&mdev->next);
123	if (!parent->mdev_driver->get_available)
124		atomic_inc(&parent->available_instances);
125	mutex_unlock(&mdev_list_lock);
126
127	/* Pairs with the get in mdev_device_create() */
128	kobject_put(&mdev->type->kobj);
129
130	dev_dbg(&mdev->dev, "MDEV: destroying\n");
131	kfree(mdev);
132}
133
134int mdev_device_create(struct mdev_type *type, const guid_t *uuid)
135{
136	int ret;
137	struct mdev_device *mdev, *tmp;
138	struct mdev_parent *parent = type->parent;
139	struct mdev_driver *drv = parent->mdev_driver;
 
 
 
 
140
141	mutex_lock(&mdev_list_lock);
142
143	/* Check for duplicate */
144	list_for_each_entry(tmp, &mdev_list, next) {
145		if (guid_equal(&tmp->uuid, uuid)) {
146			mutex_unlock(&mdev_list_lock);
147			return -EEXIST;
148		}
149	}
150
151	if (!drv->get_available) {
152		/*
153		 * Note: that non-atomic read and dec is fine here because
154		 * all modifications are under mdev_list_lock.
155		 */
156		if (!atomic_read(&parent->available_instances)) {
157			mutex_unlock(&mdev_list_lock);
158			return -EUSERS;
159		}
160		atomic_dec(&parent->available_instances);
161	}
162
163	mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
164	if (!mdev) {
165		mutex_unlock(&mdev_list_lock);
166		return -ENOMEM;
167	}
168
169	device_initialize(&mdev->dev);
170	mdev->dev.parent  = parent->dev;
171	mdev->dev.bus = &mdev_bus_type;
 
 
 
172	mdev->dev.release = mdev_device_release;
173	mdev->dev.groups = mdev_device_groups;
174	mdev->type = type;
175	/* Pairs with the put in mdev_device_release() */
176	kobject_get(&type->kobj);
177
178	guid_copy(&mdev->uuid, uuid);
179	list_add(&mdev->next, &mdev_list);
180	mutex_unlock(&mdev_list_lock);
 
 
181
182	ret = dev_set_name(&mdev->dev, "%pUl", uuid);
183	if (ret)
184		goto out_put_device;
185
186	/* Check if parent unregistration has started */
187	if (!down_read_trylock(&parent->unreg_sem)) {
188		ret = -ENODEV;
189		goto out_put_device;
190	}
191
192	ret = device_add(&mdev->dev);
193	if (ret)
194		goto out_unlock;
195
196	ret = device_driver_attach(&drv->driver, &mdev->dev);
197	if (ret)
198		goto out_del;
199
200	ret = mdev_create_sysfs_files(mdev);
201	if (ret)
202		goto out_del;
203
204	mdev->active = true;
205	dev_dbg(&mdev->dev, "MDEV: created\n");
206	up_read(&parent->unreg_sem);
207
208	return 0;
 
209
210out_del:
211	device_del(&mdev->dev);
212out_unlock:
213	up_read(&parent->unreg_sem);
214out_put_device:
215	put_device(&mdev->dev);
216	return ret;
217}
218
219int mdev_device_remove(struct mdev_device *mdev)
220{
221	struct mdev_device *tmp;
222	struct mdev_parent *parent = mdev->type->parent;
 
 
 
 
 
223
224	mutex_lock(&mdev_list_lock);
225	list_for_each_entry(tmp, &mdev_list, next) {
226		if (tmp == mdev)
 
227			break;
 
228	}
229
230	if (tmp != mdev) {
231		mutex_unlock(&mdev_list_lock);
 
 
 
 
232		return -ENODEV;
233	}
234
235	if (!mdev->active) {
 
 
 
 
 
 
 
 
 
236		mutex_unlock(&mdev_list_lock);
237		return -EAGAIN;
 
238	}
239
240	mdev->active = false;
241	mutex_unlock(&mdev_list_lock);
242
243	/* Check if parent unregistration has started */
244	if (!down_read_trylock(&parent->unreg_sem))
245		return -ENODEV;
246
247	mdev_device_remove_common(mdev);
248	up_read(&parent->unreg_sem);
249	return 0;
250}
251
252static int __init mdev_init(void)
253{
254	return bus_register(&mdev_bus_type);
255}
256
257static void __exit mdev_exit(void)
258{
259	if (mdev_bus_compat_class)
260		class_compat_unregister(mdev_bus_compat_class);
261	bus_unregister(&mdev_bus_type);
 
262}
263
264subsys_initcall(mdev_init)
265module_exit(mdev_exit)
266
267MODULE_VERSION(DRIVER_VERSION);
268MODULE_LICENSE("GPL v2");
269MODULE_AUTHOR(DRIVER_AUTHOR);
270MODULE_DESCRIPTION(DRIVER_DESC);