linux/drivers/vfio/mdev/mdev_core.c
<<
>>
Prefs
   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");
 444