Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  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");