Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Mediated device Core Driver
0004  *
0005  * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
0006  *     Author: Neo Jia <cjia@nvidia.com>
0007  *             Kirti Wankhede <kwankhede@nvidia.com>
0008  */
0009 
0010 #include <linux/module.h>
0011 #include <linux/device.h>
0012 #include <linux/slab.h>
0013 #include <linux/uuid.h>
0014 #include <linux/sysfs.h>
0015 #include <linux/mdev.h>
0016 
0017 #include "mdev_private.h"
0018 
0019 #define DRIVER_VERSION      "0.1"
0020 #define DRIVER_AUTHOR       "NVIDIA Corporation"
0021 #define DRIVER_DESC     "Mediated device Core Driver"
0022 
0023 static LIST_HEAD(parent_list);
0024 static DEFINE_MUTEX(parent_list_lock);
0025 static struct class_compat *mdev_bus_compat_class;
0026 
0027 static LIST_HEAD(mdev_list);
0028 static DEFINE_MUTEX(mdev_list_lock);
0029 
0030 struct device *mdev_parent_dev(struct mdev_device *mdev)
0031 {
0032     return mdev->type->parent->dev;
0033 }
0034 EXPORT_SYMBOL(mdev_parent_dev);
0035 
0036 /*
0037  * Return the index in supported_type_groups that this mdev_device was created
0038  * from.
0039  */
0040 unsigned int mdev_get_type_group_id(struct mdev_device *mdev)
0041 {
0042     return mdev->type->type_group_id;
0043 }
0044 EXPORT_SYMBOL(mdev_get_type_group_id);
0045 
0046 /*
0047  * Used in mdev_type_attribute sysfs functions to return the index in the
0048  * supported_type_groups that the sysfs is called from.
0049  */
0050 unsigned int mtype_get_type_group_id(struct mdev_type *mtype)
0051 {
0052     return mtype->type_group_id;
0053 }
0054 EXPORT_SYMBOL(mtype_get_type_group_id);
0055 
0056 /*
0057  * Used in mdev_type_attribute sysfs functions to return the parent struct
0058  * device
0059  */
0060 struct device *mtype_get_parent_dev(struct mdev_type *mtype)
0061 {
0062     return mtype->parent->dev;
0063 }
0064 EXPORT_SYMBOL(mtype_get_parent_dev);
0065 
0066 /* Should be called holding parent_list_lock */
0067 static struct mdev_parent *__find_parent_device(struct device *dev)
0068 {
0069     struct mdev_parent *parent;
0070 
0071     list_for_each_entry(parent, &parent_list, next) {
0072         if (parent->dev == dev)
0073             return parent;
0074     }
0075     return NULL;
0076 }
0077 
0078 void mdev_release_parent(struct kref *kref)
0079 {
0080     struct mdev_parent *parent = container_of(kref, struct mdev_parent,
0081                           ref);
0082     struct device *dev = parent->dev;
0083 
0084     kfree(parent);
0085     put_device(dev);
0086 }
0087 
0088 /* Caller must hold parent unreg_sem read or write lock */
0089 static void mdev_device_remove_common(struct mdev_device *mdev)
0090 {
0091     struct mdev_parent *parent = mdev->type->parent;
0092 
0093     mdev_remove_sysfs_files(mdev);
0094     device_del(&mdev->dev);
0095     lockdep_assert_held(&parent->unreg_sem);
0096     /* Balances with device_initialize() */
0097     put_device(&mdev->dev);
0098 }
0099 
0100 static int mdev_device_remove_cb(struct device *dev, void *data)
0101 {
0102     struct mdev_device *mdev = mdev_from_dev(dev);
0103 
0104     if (mdev)
0105         mdev_device_remove_common(mdev);
0106     return 0;
0107 }
0108 
0109 /*
0110  * mdev_register_device : Register a device
0111  * @dev: device structure representing parent device.
0112  * @mdev_driver: Device driver to bind to the newly created mdev
0113  *
0114  * Add device to list of registered parent devices.
0115  * Returns a negative value on error, otherwise 0.
0116  */
0117 int mdev_register_device(struct device *dev, struct mdev_driver *mdev_driver)
0118 {
0119     int ret;
0120     struct mdev_parent *parent;
0121     char *env_string = "MDEV_STATE=registered";
0122     char *envp[] = { env_string, NULL };
0123 
0124     /* check for mandatory ops */
0125     if (!mdev_driver->supported_type_groups)
0126         return -EINVAL;
0127 
0128     dev = get_device(dev);
0129     if (!dev)
0130         return -EINVAL;
0131 
0132     mutex_lock(&parent_list_lock);
0133 
0134     /* Check for duplicate */
0135     parent = __find_parent_device(dev);
0136     if (parent) {
0137         parent = NULL;
0138         ret = -EEXIST;
0139         goto add_dev_err;
0140     }
0141 
0142     parent = kzalloc(sizeof(*parent), GFP_KERNEL);
0143     if (!parent) {
0144         ret = -ENOMEM;
0145         goto add_dev_err;
0146     }
0147 
0148     kref_init(&parent->ref);
0149     init_rwsem(&parent->unreg_sem);
0150 
0151     parent->dev = dev;
0152     parent->mdev_driver = mdev_driver;
0153 
0154     if (!mdev_bus_compat_class) {
0155         mdev_bus_compat_class = class_compat_register("mdev_bus");
0156         if (!mdev_bus_compat_class) {
0157             ret = -ENOMEM;
0158             goto add_dev_err;
0159         }
0160     }
0161 
0162     ret = parent_create_sysfs_files(parent);
0163     if (ret)
0164         goto add_dev_err;
0165 
0166     ret = class_compat_create_link(mdev_bus_compat_class, dev, NULL);
0167     if (ret)
0168         dev_warn(dev, "Failed to create compatibility class link\n");
0169 
0170     list_add(&parent->next, &parent_list);
0171     mutex_unlock(&parent_list_lock);
0172 
0173     dev_info(dev, "MDEV: Registered\n");
0174     kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
0175 
0176     return 0;
0177 
0178 add_dev_err:
0179     mutex_unlock(&parent_list_lock);
0180     if (parent)
0181         mdev_put_parent(parent);
0182     else
0183         put_device(dev);
0184     return ret;
0185 }
0186 EXPORT_SYMBOL(mdev_register_device);
0187 
0188 /*
0189  * mdev_unregister_device : Unregister a parent device
0190  * @dev: device structure representing parent device.
0191  *
0192  * Remove device from list of registered parent devices. Give a chance to free
0193  * existing mediated devices for given device.
0194  */
0195 
0196 void mdev_unregister_device(struct device *dev)
0197 {
0198     struct mdev_parent *parent;
0199     char *env_string = "MDEV_STATE=unregistered";
0200     char *envp[] = { env_string, NULL };
0201 
0202     mutex_lock(&parent_list_lock);
0203     parent = __find_parent_device(dev);
0204 
0205     if (!parent) {
0206         mutex_unlock(&parent_list_lock);
0207         return;
0208     }
0209     dev_info(dev, "MDEV: Unregistering\n");
0210 
0211     list_del(&parent->next);
0212     mutex_unlock(&parent_list_lock);
0213 
0214     down_write(&parent->unreg_sem);
0215 
0216     class_compat_remove_link(mdev_bus_compat_class, dev, NULL);
0217 
0218     device_for_each_child(dev, NULL, mdev_device_remove_cb);
0219 
0220     parent_remove_sysfs_files(parent);
0221     up_write(&parent->unreg_sem);
0222 
0223     mdev_put_parent(parent);
0224 
0225     /* We still have the caller's reference to use for the uevent */
0226     kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
0227 }
0228 EXPORT_SYMBOL(mdev_unregister_device);
0229 
0230 static void mdev_device_release(struct device *dev)
0231 {
0232     struct mdev_device *mdev = to_mdev_device(dev);
0233 
0234     /* Pairs with the get in mdev_device_create() */
0235     kobject_put(&mdev->type->kobj);
0236 
0237     mutex_lock(&mdev_list_lock);
0238     list_del(&mdev->next);
0239     mutex_unlock(&mdev_list_lock);
0240 
0241     dev_dbg(&mdev->dev, "MDEV: destroying\n");
0242     kfree(mdev);
0243 }
0244 
0245 int mdev_device_create(struct mdev_type *type, const guid_t *uuid)
0246 {
0247     int ret;
0248     struct mdev_device *mdev, *tmp;
0249     struct mdev_parent *parent = type->parent;
0250     struct mdev_driver *drv = parent->mdev_driver;
0251 
0252     mutex_lock(&mdev_list_lock);
0253 
0254     /* Check for duplicate */
0255     list_for_each_entry(tmp, &mdev_list, next) {
0256         if (guid_equal(&tmp->uuid, uuid)) {
0257             mutex_unlock(&mdev_list_lock);
0258             return -EEXIST;
0259         }
0260     }
0261 
0262     mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
0263     if (!mdev) {
0264         mutex_unlock(&mdev_list_lock);
0265         return -ENOMEM;
0266     }
0267 
0268     device_initialize(&mdev->dev);
0269     mdev->dev.parent  = parent->dev;
0270     mdev->dev.bus = &mdev_bus_type;
0271     mdev->dev.release = mdev_device_release;
0272     mdev->dev.groups = mdev_device_groups;
0273     mdev->type = type;
0274     /* Pairs with the put in mdev_device_release() */
0275     kobject_get(&type->kobj);
0276 
0277     guid_copy(&mdev->uuid, uuid);
0278     list_add(&mdev->next, &mdev_list);
0279     mutex_unlock(&mdev_list_lock);
0280 
0281     ret = dev_set_name(&mdev->dev, "%pUl", uuid);
0282     if (ret)
0283         goto out_put_device;
0284 
0285     /* Check if parent unregistration has started */
0286     if (!down_read_trylock(&parent->unreg_sem)) {
0287         ret = -ENODEV;
0288         goto out_put_device;
0289     }
0290 
0291     ret = device_add(&mdev->dev);
0292     if (ret)
0293         goto out_unlock;
0294 
0295     ret = device_driver_attach(&drv->driver, &mdev->dev);
0296     if (ret)
0297         goto out_del;
0298 
0299     ret = mdev_create_sysfs_files(mdev);
0300     if (ret)
0301         goto out_del;
0302 
0303     mdev->active = true;
0304     dev_dbg(&mdev->dev, "MDEV: created\n");
0305     up_read(&parent->unreg_sem);
0306 
0307     return 0;
0308 
0309 out_del:
0310     device_del(&mdev->dev);
0311 out_unlock:
0312     up_read(&parent->unreg_sem);
0313 out_put_device:
0314     put_device(&mdev->dev);
0315     return ret;
0316 }
0317 
0318 int mdev_device_remove(struct mdev_device *mdev)
0319 {
0320     struct mdev_device *tmp;
0321     struct mdev_parent *parent = mdev->type->parent;
0322 
0323     mutex_lock(&mdev_list_lock);
0324     list_for_each_entry(tmp, &mdev_list, next) {
0325         if (tmp == mdev)
0326             break;
0327     }
0328 
0329     if (tmp != mdev) {
0330         mutex_unlock(&mdev_list_lock);
0331         return -ENODEV;
0332     }
0333 
0334     if (!mdev->active) {
0335         mutex_unlock(&mdev_list_lock);
0336         return -EAGAIN;
0337     }
0338 
0339     mdev->active = false;
0340     mutex_unlock(&mdev_list_lock);
0341 
0342     /* Check if parent unregistration has started */
0343     if (!down_read_trylock(&parent->unreg_sem))
0344         return -ENODEV;
0345 
0346     mdev_device_remove_common(mdev);
0347     up_read(&parent->unreg_sem);
0348     return 0;
0349 }
0350 
0351 static int __init mdev_init(void)
0352 {
0353     return bus_register(&mdev_bus_type);
0354 }
0355 
0356 static void __exit mdev_exit(void)
0357 {
0358     if (mdev_bus_compat_class)
0359         class_compat_unregister(mdev_bus_compat_class);
0360     bus_unregister(&mdev_bus_type);
0361 }
0362 
0363 subsys_initcall(mdev_init)
0364 module_exit(mdev_exit)
0365 
0366 MODULE_VERSION(DRIVER_VERSION);
0367 MODULE_LICENSE("GPL v2");
0368 MODULE_AUTHOR(DRIVER_AUTHOR);
0369 MODULE_DESCRIPTION(DRIVER_DESC);