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