xref: /linux/drivers/vfio/mdev/mdev_core.c (revision cdd5b5a9761fd66d17586e4f4ba6588c70e640ea)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
27b96953bSKirti Wankhede /*
37b96953bSKirti Wankhede  * Mediated device Core Driver
47b96953bSKirti Wankhede  *
57b96953bSKirti Wankhede  * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
67b96953bSKirti Wankhede  *     Author: Neo Jia <cjia@nvidia.com>
77b96953bSKirti Wankhede  *             Kirti Wankhede <kwankhede@nvidia.com>
87b96953bSKirti Wankhede  */
97b96953bSKirti Wankhede 
107b96953bSKirti Wankhede #include <linux/module.h>
117b96953bSKirti Wankhede #include <linux/slab.h>
127b96953bSKirti Wankhede #include <linux/sysfs.h>
137b96953bSKirti Wankhede #include <linux/mdev.h>
147b96953bSKirti Wankhede 
157b96953bSKirti Wankhede #include "mdev_private.h"
167b96953bSKirti Wankhede 
177b96953bSKirti Wankhede #define DRIVER_VERSION		"0.1"
187b96953bSKirti Wankhede #define DRIVER_AUTHOR		"NVIDIA Corporation"
197b96953bSKirti Wankhede #define DRIVER_DESC		"Mediated device Core Driver"
207b96953bSKirti Wankhede 
217b96953bSKirti Wankhede static struct class_compat *mdev_bus_compat_class;
227b96953bSKirti Wankhede 
2349550787SAlex Williamson static LIST_HEAD(mdev_list);
2449550787SAlex Williamson static DEFINE_MUTEX(mdev_list_lock);
2549550787SAlex Williamson 
265715c4ddSParav Pandit /* Caller must hold parent unreg_sem read or write lock */
mdev_device_remove_common(struct mdev_device * mdev)275715c4ddSParav Pandit static void mdev_device_remove_common(struct mdev_device *mdev)
285715c4ddSParav Pandit {
29fbea4323SJason Gunthorpe 	struct mdev_parent *parent = mdev->type->parent;
305715c4ddSParav Pandit 
31417fd5bfSJason Gunthorpe 	mdev_remove_sysfs_files(mdev);
325715c4ddSParav Pandit 	device_del(&mdev->dev);
335715c4ddSParav Pandit 	lockdep_assert_held(&parent->unreg_sem);
345715c4ddSParav Pandit 	/* Balances with device_initialize() */
355715c4ddSParav Pandit 	put_device(&mdev->dev);
365715c4ddSParav Pandit }
375715c4ddSParav Pandit 
mdev_device_remove_cb(struct device * dev,void * data)387b96953bSKirti Wankhede static int mdev_device_remove_cb(struct device *dev, void *data)
397b96953bSKirti Wankhede {
40cbf3bb28SChristoph Hellwig 	if (dev->bus == &mdev_bus_type)
41cbf3bb28SChristoph Hellwig 		mdev_device_remove_common(to_mdev_device(dev));
426093e348SParav Pandit 	return 0;
437b96953bSKirti Wankhede }
447b96953bSKirti Wankhede 
457b96953bSKirti Wankhede /*
4689345d51SChristoph Hellwig  * mdev_register_parent: Register a device as parent for mdevs
4789345d51SChristoph Hellwig  * @parent: parent structure registered
487b96953bSKirti Wankhede  * @dev: device structure representing parent device.
496b42f491SJason Gunthorpe  * @mdev_driver: Device driver to bind to the newly created mdev
50da44c340SChristoph Hellwig  * @types: Array of supported mdev types
51da44c340SChristoph Hellwig  * @nr_types: Number of entries in @types
527b96953bSKirti Wankhede  *
5389345d51SChristoph Hellwig  * Registers the @parent stucture as a parent for mdev types and thus mdev
5489345d51SChristoph Hellwig  * devices.  The caller needs to hold a reference on @dev that must not be
5589345d51SChristoph Hellwig  * released until after the call to mdev_unregister_parent().
5689345d51SChristoph Hellwig  *
577b96953bSKirti Wankhede  * Returns a negative value on error, otherwise 0.
587b96953bSKirti Wankhede  */
mdev_register_parent(struct mdev_parent * parent,struct device * dev,struct mdev_driver * mdev_driver,struct mdev_type ** types,unsigned int nr_types)5989345d51SChristoph Hellwig int mdev_register_parent(struct mdev_parent *parent, struct device *dev,
60da44c340SChristoph Hellwig 		struct mdev_driver *mdev_driver, struct mdev_type **types,
61da44c340SChristoph Hellwig 		unsigned int nr_types)
627b96953bSKirti Wankhede {
631e4d09d2SAlex Williamson 	char *env_string = "MDEV_STATE=registered";
641e4d09d2SAlex Williamson 	char *envp[] = { env_string, NULL };
6589345d51SChristoph Hellwig 	int ret;
667b96953bSKirti Wankhede 
6789345d51SChristoph Hellwig 	memset(parent, 0, sizeof(*parent));
685715c4ddSParav Pandit 	init_rwsem(&parent->unreg_sem);
697b96953bSKirti Wankhede 	parent->dev = dev;
706b42f491SJason Gunthorpe 	parent->mdev_driver = mdev_driver;
71da44c340SChristoph Hellwig 	parent->types = types;
72da44c340SChristoph Hellwig 	parent->nr_types = nr_types;
739c799c22SJason Gunthorpe 	atomic_set(&parent->available_instances, mdev_driver->max_instances);
747b96953bSKirti Wankhede 
757b96953bSKirti Wankhede 	ret = parent_create_sysfs_files(parent);
767b96953bSKirti Wankhede 	if (ret)
7789345d51SChristoph Hellwig 		return ret;
787b96953bSKirti Wankhede 
797b96953bSKirti Wankhede 	ret = class_compat_create_link(mdev_bus_compat_class, dev, NULL);
807b96953bSKirti Wankhede 	if (ret)
817b96953bSKirti Wankhede 		dev_warn(dev, "Failed to create compatibility class link\n");
827b96953bSKirti Wankhede 
837b96953bSKirti Wankhede 	dev_info(dev, "MDEV: Registered\n");
841e4d09d2SAlex Williamson 	kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
857b96953bSKirti Wankhede 	return 0;
867b96953bSKirti Wankhede }
8789345d51SChristoph Hellwig EXPORT_SYMBOL(mdev_register_parent);
887b96953bSKirti Wankhede 
897b96953bSKirti Wankhede /*
9089345d51SChristoph Hellwig  * mdev_unregister_parent : Unregister a parent device
9189345d51SChristoph Hellwig  * @parent: parent structure to unregister
927b96953bSKirti Wankhede  */
mdev_unregister_parent(struct mdev_parent * parent)9389345d51SChristoph Hellwig void mdev_unregister_parent(struct mdev_parent *parent)
947b96953bSKirti Wankhede {
951e4d09d2SAlex Williamson 	char *env_string = "MDEV_STATE=unregistered";
961e4d09d2SAlex Williamson 	char *envp[] = { env_string, NULL };
977b96953bSKirti Wankhede 
9889345d51SChristoph Hellwig 	dev_info(parent->dev, "MDEV: Unregistering\n");
995715c4ddSParav Pandit 
1005715c4ddSParav Pandit 	down_write(&parent->unreg_sem);
10189345d51SChristoph Hellwig 	class_compat_remove_link(mdev_bus_compat_class, parent->dev, NULL);
10289345d51SChristoph Hellwig 	device_for_each_child(parent->dev, NULL, mdev_device_remove_cb);
1037b96953bSKirti Wankhede 	parent_remove_sysfs_files(parent);
1045715c4ddSParav Pandit 	up_write(&parent->unreg_sem);
1057b96953bSKirti Wankhede 
10689345d51SChristoph Hellwig 	kobject_uevent_env(&parent->dev->kobj, KOBJ_CHANGE, envp);
1077b96953bSKirti Wankhede }
10889345d51SChristoph Hellwig EXPORT_SYMBOL(mdev_unregister_parent);
1097b96953bSKirti Wankhede 
mdev_device_release(struct device * dev)110fbd0e2b0SJason Gunthorpe static void mdev_device_release(struct device *dev)
1117b96953bSKirti Wankhede {
112fbd0e2b0SJason Gunthorpe 	struct mdev_device *mdev = to_mdev_device(dev);
1139c799c22SJason Gunthorpe 	struct mdev_parent *parent = mdev->type->parent;
114fbd0e2b0SJason Gunthorpe 
115002fe996SAlex Williamson 	mutex_lock(&mdev_list_lock);
116002fe996SAlex Williamson 	list_del(&mdev->next);
1179c799c22SJason Gunthorpe 	if (!parent->mdev_driver->get_available)
1189c799c22SJason Gunthorpe 		atomic_inc(&parent->available_instances);
119002fe996SAlex Williamson 	mutex_unlock(&mdev_list_lock);
120002fe996SAlex Williamson 
1219c799c22SJason Gunthorpe 	/* Pairs with the get in mdev_device_create() */
1229c799c22SJason Gunthorpe 	kobject_put(&mdev->type->kobj);
1239c799c22SJason Gunthorpe 
1247b96953bSKirti Wankhede 	dev_dbg(&mdev->dev, "MDEV: destroying\n");
1257b96953bSKirti Wankhede 	kfree(mdev);
1267b96953bSKirti Wankhede }
1277b96953bSKirti Wankhede 
mdev_device_create(struct mdev_type * type,const guid_t * uuid)128417fd5bfSJason Gunthorpe int mdev_device_create(struct mdev_type *type, const guid_t *uuid)
1297b96953bSKirti Wankhede {
1307b96953bSKirti Wankhede 	int ret;
131002fe996SAlex Williamson 	struct mdev_device *mdev, *tmp;
132a9f8111dSJason Gunthorpe 	struct mdev_parent *parent = type->parent;
1336b42f491SJason Gunthorpe 	struct mdev_driver *drv = parent->mdev_driver;
1347b96953bSKirti Wankhede 
135002fe996SAlex Williamson 	mutex_lock(&mdev_list_lock);
1367b96953bSKirti Wankhede 
1377b96953bSKirti Wankhede 	/* Check for duplicate */
138002fe996SAlex Williamson 	list_for_each_entry(tmp, &mdev_list, next) {
139278bca7fSAndy Shevchenko 		if (guid_equal(&tmp->uuid, uuid)) {
140002fe996SAlex Williamson 			mutex_unlock(&mdev_list_lock);
141fbd0e2b0SJason Gunthorpe 			return -EEXIST;
142002fe996SAlex Williamson 		}
1437b96953bSKirti Wankhede 	}
1447b96953bSKirti Wankhede 
1459c799c22SJason Gunthorpe 	if (!drv->get_available) {
1469c799c22SJason Gunthorpe 		/*
1479c799c22SJason Gunthorpe 		 * Note: that non-atomic read and dec is fine here because
1489c799c22SJason Gunthorpe 		 * all modifications are under mdev_list_lock.
1499c799c22SJason Gunthorpe 		 */
1509c799c22SJason Gunthorpe 		if (!atomic_read(&parent->available_instances)) {
1519c799c22SJason Gunthorpe 			mutex_unlock(&mdev_list_lock);
1529c799c22SJason Gunthorpe 			return -EUSERS;
1539c799c22SJason Gunthorpe 		}
1549c799c22SJason Gunthorpe 		atomic_dec(&parent->available_instances);
1559c799c22SJason Gunthorpe 	}
1569c799c22SJason Gunthorpe 
1577b96953bSKirti Wankhede 	mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
1587b96953bSKirti Wankhede 	if (!mdev) {
159002fe996SAlex Williamson 		mutex_unlock(&mdev_list_lock);
160fbd0e2b0SJason Gunthorpe 		return -ENOMEM;
1615715c4ddSParav Pandit 	}
1625715c4ddSParav Pandit 
163522ecce0SParav Pandit 	device_initialize(&mdev->dev);
164417fd5bfSJason Gunthorpe 	mdev->dev.parent  = parent->dev;
1657b96953bSKirti Wankhede 	mdev->dev.bus = &mdev_bus_type;
1667b96953bSKirti Wankhede 	mdev->dev.release = mdev_device_release;
1672aa72ec9SJason Gunthorpe 	mdev->dev.groups = mdev_device_groups;
168417fd5bfSJason Gunthorpe 	mdev->type = type;
169fbd0e2b0SJason Gunthorpe 	/* Pairs with the put in mdev_device_release() */
170fbea4323SJason Gunthorpe 	kobject_get(&type->kobj);
171fbd0e2b0SJason Gunthorpe 
172fbd0e2b0SJason Gunthorpe 	guid_copy(&mdev->uuid, uuid);
173fbd0e2b0SJason Gunthorpe 	list_add(&mdev->next, &mdev_list);
174fbd0e2b0SJason Gunthorpe 	mutex_unlock(&mdev_list_lock);
175fbd0e2b0SJason Gunthorpe 
17618d73124SJason Gunthorpe 	ret = dev_set_name(&mdev->dev, "%pUl", uuid);
17718d73124SJason Gunthorpe 	if (ret)
17818d73124SJason Gunthorpe 		goto out_put_device;
179fbd0e2b0SJason Gunthorpe 
180fbd0e2b0SJason Gunthorpe 	/* Check if parent unregistration has started */
181fbd0e2b0SJason Gunthorpe 	if (!down_read_trylock(&parent->unreg_sem)) {
182fbd0e2b0SJason Gunthorpe 		ret = -ENODEV;
183fbd0e2b0SJason Gunthorpe 		goto out_put_device;
184fbd0e2b0SJason Gunthorpe 	}
1857b96953bSKirti Wankhede 
186522ecce0SParav Pandit 	ret = device_add(&mdev->dev);
187522ecce0SParav Pandit 	if (ret)
1886c7f98b3SJason Gunthorpe 		goto out_unlock;
1897b96953bSKirti Wankhede 
19088a21f26SJason Gunthorpe 	ret = device_driver_attach(&drv->driver, &mdev->dev);
19188a21f26SJason Gunthorpe 	if (ret)
19288a21f26SJason Gunthorpe 		goto out_del;
19388a21f26SJason Gunthorpe 
194417fd5bfSJason Gunthorpe 	ret = mdev_create_sysfs_files(mdev);
195522ecce0SParav Pandit 	if (ret)
196fbd0e2b0SJason Gunthorpe 		goto out_del;
1977b96953bSKirti Wankhede 
198002fe996SAlex Williamson 	mdev->active = true;
1997b96953bSKirti Wankhede 	dev_dbg(&mdev->dev, "MDEV: created\n");
2005715c4ddSParav Pandit 	up_read(&parent->unreg_sem);
2017b96953bSKirti Wankhede 
202002fe996SAlex Williamson 	return 0;
20349550787SAlex Williamson 
204fbd0e2b0SJason Gunthorpe out_del:
205522ecce0SParav Pandit 	device_del(&mdev->dev);
206fbd0e2b0SJason Gunthorpe out_unlock:
2075715c4ddSParav Pandit 	up_read(&parent->unreg_sem);
208fbd0e2b0SJason Gunthorpe out_put_device:
209522ecce0SParav Pandit 	put_device(&mdev->dev);
2107b96953bSKirti Wankhede 	return ret;
2117b96953bSKirti Wankhede }
2127b96953bSKirti Wankhede 
mdev_device_remove(struct mdev_device * mdev)2132a3d15f2SJason Gunthorpe int mdev_device_remove(struct mdev_device *mdev)
2147b96953bSKirti Wankhede {
2152a3d15f2SJason Gunthorpe 	struct mdev_device *tmp;
216fbea4323SJason Gunthorpe 	struct mdev_parent *parent = mdev->type->parent;
2177b96953bSKirti Wankhede 
21849550787SAlex Williamson 	mutex_lock(&mdev_list_lock);
21949550787SAlex Williamson 	list_for_each_entry(tmp, &mdev_list, next) {
220002fe996SAlex Williamson 		if (tmp == mdev)
22149550787SAlex Williamson 			break;
22249550787SAlex Williamson 	}
223002fe996SAlex Williamson 
224002fe996SAlex Williamson 	if (tmp != mdev) {
225002fe996SAlex Williamson 		mutex_unlock(&mdev_list_lock);
226002fe996SAlex Williamson 		return -ENODEV;
22749550787SAlex Williamson 	}
22849550787SAlex Williamson 
229002fe996SAlex Williamson 	if (!mdev->active) {
23049550787SAlex Williamson 		mutex_unlock(&mdev_list_lock);
231002fe996SAlex Williamson 		return -EAGAIN;
232002fe996SAlex Williamson 	}
23349550787SAlex Williamson 
234002fe996SAlex Williamson 	mdev->active = false;
235002fe996SAlex Williamson 	mutex_unlock(&mdev_list_lock);
23649550787SAlex Williamson 
2375715c4ddSParav Pandit 	/* Check if parent unregistration has started */
2385715c4ddSParav Pandit 	if (!down_read_trylock(&parent->unreg_sem))
2395715c4ddSParav Pandit 		return -ENODEV;
240522ecce0SParav Pandit 
2415715c4ddSParav Pandit 	mdev_device_remove_common(mdev);
2425715c4ddSParav Pandit 	up_read(&parent->unreg_sem);
24349550787SAlex Williamson 	return 0;
2447b96953bSKirti Wankhede }
2457b96953bSKirti Wankhede 
mdev_init(void)2467b96953bSKirti Wankhede static int __init mdev_init(void)
2477b96953bSKirti Wankhede {
248*ff598081SEric Farman 	int ret;
249*ff598081SEric Farman 
250*ff598081SEric Farman 	ret = bus_register(&mdev_bus_type);
251*ff598081SEric Farman 	if (ret)
252*ff598081SEric Farman 		return ret;
253*ff598081SEric Farman 
254*ff598081SEric Farman 	mdev_bus_compat_class = class_compat_register("mdev_bus");
255*ff598081SEric Farman 	if (!mdev_bus_compat_class) {
256*ff598081SEric Farman 		bus_unregister(&mdev_bus_type);
257*ff598081SEric Farman 		return -ENOMEM;
258*ff598081SEric Farman 	}
259*ff598081SEric Farman 
260*ff598081SEric Farman 	return 0;
2617b96953bSKirti Wankhede }
2627b96953bSKirti Wankhede 
mdev_exit(void)2637b96953bSKirti Wankhede static void __exit mdev_exit(void)
2647b96953bSKirti Wankhede {
2657b96953bSKirti Wankhede 	class_compat_unregister(mdev_bus_compat_class);
2666c7f98b3SJason Gunthorpe 	bus_unregister(&mdev_bus_type);
2677b96953bSKirti Wankhede }
2687b96953bSKirti Wankhede 
26915a5896eSChristoph Hellwig subsys_initcall(mdev_init)
2707b96953bSKirti Wankhede module_exit(mdev_exit)
2717b96953bSKirti Wankhede 
2727b96953bSKirti Wankhede MODULE_VERSION(DRIVER_VERSION);
2737b96953bSKirti Wankhede MODULE_LICENSE("GPL v2");
2747b96953bSKirti Wankhede MODULE_AUTHOR(DRIVER_AUTHOR);
2757b96953bSKirti Wankhede MODULE_DESCRIPTION(DRIVER_DESC);
276