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.13.7
  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	ret = parent_create_sysfs_files(parent);
 76	if (ret)
 77		return ret;
 78
 79	ret = class_compat_create_link(mdev_bus_compat_class, dev, NULL);
 80	if (ret)
 81		dev_warn(dev, "Failed to create compatibility class link\n");
 82
 
 
 
 83	dev_info(dev, "MDEV: Registered\n");
 84	kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
 85	return 0;
 
 
 
 
 
 
 
 
 86}
 87EXPORT_SYMBOL(mdev_register_parent);
 88
 89/*
 90 * mdev_unregister_parent : Unregister a parent device
 91 * @parent: parent structure to unregister
 
 
 
 92 */
 93void mdev_unregister_parent(struct mdev_parent *parent)
 
 94{
 95	char *env_string = "MDEV_STATE=unregistered";
 96	char *envp[] = { env_string, NULL };
 
 
 
 
 
 
 
 
 
 
 
 
 97
 98	dev_info(parent->dev, "MDEV: Unregistering\n");
 
 99
100	down_write(&parent->unreg_sem);
101	class_compat_remove_link(mdev_bus_compat_class, parent->dev, NULL);
102	device_for_each_child(parent->dev, NULL, mdev_device_remove_cb);
103	parent_remove_sysfs_files(parent);
104	up_write(&parent->unreg_sem);
105
106	kobject_uevent_env(&parent->dev->kobj, KOBJ_CHANGE, envp);
 
107}
108EXPORT_SYMBOL(mdev_unregister_parent);
109
110static void mdev_device_release(struct device *dev)
111{
112	struct mdev_device *mdev = to_mdev_device(dev);
113	struct mdev_parent *parent = mdev->type->parent;
114
115	mutex_lock(&mdev_list_lock);
116	list_del(&mdev->next);
117	if (!parent->mdev_driver->get_available)
118		atomic_inc(&parent->available_instances);
119	mutex_unlock(&mdev_list_lock);
120
121	/* Pairs with the get in mdev_device_create() */
122	kobject_put(&mdev->type->kobj);
123
124	dev_dbg(&mdev->dev, "MDEV: destroying\n");
125	kfree(mdev);
126}
127
128int mdev_device_create(struct mdev_type *type, const guid_t *uuid)
129{
130	int ret;
131	struct mdev_device *mdev, *tmp;
132	struct mdev_parent *parent = type->parent;
133	struct mdev_driver *drv = parent->mdev_driver;
 
 
 
 
134
135	mutex_lock(&mdev_list_lock);
136
137	/* Check for duplicate */
138	list_for_each_entry(tmp, &mdev_list, next) {
139		if (guid_equal(&tmp->uuid, uuid)) {
140			mutex_unlock(&mdev_list_lock);
141			return -EEXIST;
142		}
143	}
144
145	if (!drv->get_available) {
146		/*
147		 * Note: that non-atomic read and dec is fine here because
148		 * all modifications are under mdev_list_lock.
149		 */
150		if (!atomic_read(&parent->available_instances)) {
151			mutex_unlock(&mdev_list_lock);
152			return -EUSERS;
153		}
154		atomic_dec(&parent->available_instances);
155	}
156
157	mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
158	if (!mdev) {
159		mutex_unlock(&mdev_list_lock);
160		return -ENOMEM;
161	}
162
163	device_initialize(&mdev->dev);
164	mdev->dev.parent  = parent->dev;
165	mdev->dev.bus = &mdev_bus_type;
 
 
 
166	mdev->dev.release = mdev_device_release;
167	mdev->dev.groups = mdev_device_groups;
168	mdev->type = type;
169	/* Pairs with the put in mdev_device_release() */
170	kobject_get(&type->kobj);
171
172	guid_copy(&mdev->uuid, uuid);
173	list_add(&mdev->next, &mdev_list);
174	mutex_unlock(&mdev_list_lock);
 
 
175
176	ret = dev_set_name(&mdev->dev, "%pUl", uuid);
177	if (ret)
178		goto out_put_device;
179
180	/* Check if parent unregistration has started */
181	if (!down_read_trylock(&parent->unreg_sem)) {
182		ret = -ENODEV;
183		goto out_put_device;
184	}
185
186	ret = device_add(&mdev->dev);
187	if (ret)
188		goto out_unlock;
189
190	ret = device_driver_attach(&drv->driver, &mdev->dev);
191	if (ret)
192		goto out_del;
193
194	ret = mdev_create_sysfs_files(mdev);
195	if (ret)
196		goto out_del;
197
198	mdev->active = true;
199	dev_dbg(&mdev->dev, "MDEV: created\n");
200	up_read(&parent->unreg_sem);
201
202	return 0;
 
203
204out_del:
205	device_del(&mdev->dev);
206out_unlock:
207	up_read(&parent->unreg_sem);
208out_put_device:
209	put_device(&mdev->dev);
210	return ret;
211}
212
213int mdev_device_remove(struct mdev_device *mdev)
214{
215	struct mdev_device *tmp;
216	struct mdev_parent *parent = mdev->type->parent;
 
 
 
 
 
217
218	mutex_lock(&mdev_list_lock);
219	list_for_each_entry(tmp, &mdev_list, next) {
220		if (tmp == mdev)
 
221			break;
 
222	}
223
224	if (tmp != mdev) {
225		mutex_unlock(&mdev_list_lock);
 
 
 
 
226		return -ENODEV;
227	}
228
229	if (!mdev->active) {
 
 
 
 
 
 
 
 
 
230		mutex_unlock(&mdev_list_lock);
231		return -EAGAIN;
 
232	}
233
234	mdev->active = false;
235	mutex_unlock(&mdev_list_lock);
 
 
236
237	/* Check if parent unregistration has started */
238	if (!down_read_trylock(&parent->unreg_sem))
239		return -ENODEV;
240
241	mdev_device_remove_common(mdev);
242	up_read(&parent->unreg_sem);
243	return 0;
244}
245
246static int __init mdev_init(void)
247{
248	int ret;
249
250	ret = bus_register(&mdev_bus_type);
251	if (ret)
252		return ret;
253
254	mdev_bus_compat_class = class_compat_register("mdev_bus");
255	if (!mdev_bus_compat_class) {
256		bus_unregister(&mdev_bus_type);
257		return -ENOMEM;
258	}
259
260	return 0;
261}
262
263static void __exit mdev_exit(void)
264{
265	class_compat_unregister(mdev_bus_compat_class);
266	bus_unregister(&mdev_bus_type);
 
 
267}
268
269subsys_initcall(mdev_init)
270module_exit(mdev_exit)
271
272MODULE_VERSION(DRIVER_VERSION);
273MODULE_LICENSE("GPL v2");
274MODULE_AUTHOR(DRIVER_AUTHOR);
275MODULE_DESCRIPTION(DRIVER_DESC);