]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - drivers/vfio/mdev/mdev_core.c
vfio/mdev: Check globally for duplicate devices
[mirror_ubuntu-bionic-kernel.git] / drivers / vfio / mdev / mdev_core.c
index 126991046eb739f331d66e33bb56cd9bfb91c986..0212f0ee8aea7577246c01c99821e0ba12cf9373 100644 (file)
@@ -66,34 +66,6 @@ uuid_le mdev_uuid(struct mdev_device *mdev)
 }
 EXPORT_SYMBOL(mdev_uuid);
 
-static int _find_mdev_device(struct device *dev, void *data)
-{
-       struct mdev_device *mdev;
-
-       if (!dev_is_mdev(dev))
-               return 0;
-
-       mdev = to_mdev_device(dev);
-
-       if (uuid_le_cmp(mdev->uuid, *(uuid_le *)data) == 0)
-               return 1;
-
-       return 0;
-}
-
-static bool mdev_device_exist(struct mdev_parent *parent, uuid_le uuid)
-{
-       struct device *dev;
-
-       dev = device_find_child(parent->dev, &uuid, _find_mdev_device);
-       if (dev) {
-               put_device(dev);
-               return true;
-       }
-
-       return false;
-}
-
 /* Should be called holding parent_list_lock */
 static struct mdev_parent *__find_parent_device(struct device *dev)
 {
@@ -221,7 +193,6 @@ int mdev_register_device(struct device *dev, const struct mdev_parent_ops *ops)
        }
 
        kref_init(&parent->ref);
-       mutex_init(&parent->lock);
 
        parent->dev = dev;
        parent->ops = ops;
@@ -297,6 +268,10 @@ static void mdev_device_release(struct device *dev)
 {
        struct mdev_device *mdev = to_mdev_device(dev);
 
+       mutex_lock(&mdev_list_lock);
+       list_del(&mdev->next);
+       mutex_unlock(&mdev_list_lock);
+
        dev_dbg(&mdev->dev, "MDEV: destroying\n");
        kfree(mdev);
 }
@@ -304,7 +279,7 @@ static void mdev_device_release(struct device *dev)
 int mdev_device_create(struct kobject *kobj, struct device *dev, uuid_le uuid)
 {
        int ret;
-       struct mdev_device *mdev;
+       struct mdev_device *mdev, *tmp;
        struct mdev_parent *parent;
        struct mdev_type *type = to_mdev_type(kobj);
 
@@ -312,21 +287,28 @@ int mdev_device_create(struct kobject *kobj, struct device *dev, uuid_le uuid)
        if (!parent)
                return -EINVAL;
 
-       mutex_lock(&parent->lock);
+       mutex_lock(&mdev_list_lock);
 
        /* Check for duplicate */
-       if (mdev_device_exist(parent, uuid)) {
-               ret = -EEXIST;
-               goto create_err;
+       list_for_each_entry(tmp, &mdev_list, next) {
+               if (!uuid_le_cmp(tmp->uuid, uuid)) {
+                       mutex_unlock(&mdev_list_lock);
+                       ret = -EEXIST;
+                       goto mdev_fail;
+               }
        }
 
        mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
        if (!mdev) {
+               mutex_unlock(&mdev_list_lock);
                ret = -ENOMEM;
-               goto create_err;
+               goto mdev_fail;
        }
 
        memcpy(&mdev->uuid, &uuid, sizeof(uuid_le));
+       list_add(&mdev->next, &mdev_list);
+       mutex_unlock(&mdev_list_lock);
+
        mdev->parent = parent;
        kref_init(&mdev->ref);
 
@@ -338,35 +320,28 @@ int mdev_device_create(struct kobject *kobj, struct device *dev, uuid_le uuid)
        ret = device_register(&mdev->dev);
        if (ret) {
                put_device(&mdev->dev);
-               goto create_err;
+               goto mdev_fail;
        }
 
        ret = mdev_device_create_ops(kobj, mdev);
        if (ret)
-               goto create_failed;
+               goto create_fail;
 
        ret = mdev_create_sysfs_files(&mdev->dev, type);
        if (ret) {
                mdev_device_remove_ops(mdev, true);
-               goto create_failed;
+               goto create_fail;
        }
 
        mdev->type_kobj = kobj;
+       mdev->active = true;
        dev_dbg(&mdev->dev, "MDEV: created\n");
 
-       mutex_unlock(&parent->lock);
-
-       mutex_lock(&mdev_list_lock);
-       list_add(&mdev->next, &mdev_list);
-       mutex_unlock(&mdev_list_lock);
-
-       return ret;
+       return 0;
 
-create_failed:
+create_fail:
        device_unregister(&mdev->dev);
-
-create_err:
-       mutex_unlock(&parent->lock);
+mdev_fail:
        mdev_put_parent(parent);
        return ret;
 }
@@ -377,44 +352,39 @@ int mdev_device_remove(struct device *dev, bool force_remove)
        struct mdev_parent *parent;
        struct mdev_type *type;
        int ret;
-       bool found = false;
 
        mdev = to_mdev_device(dev);
 
        mutex_lock(&mdev_list_lock);
        list_for_each_entry(tmp, &mdev_list, next) {
-               if (tmp == mdev) {
-                       found = true;
+               if (tmp == mdev)
                        break;
-               }
        }
 
-       if (found)
-               list_del(&mdev->next);
+       if (tmp != mdev) {
+               mutex_unlock(&mdev_list_lock);
+               return -ENODEV;
+       }
 
-       mutex_unlock(&mdev_list_lock);
+       if (!mdev->active) {
+               mutex_unlock(&mdev_list_lock);
+               return -EAGAIN;
+       }
 
-       if (!found)
-               return -ENODEV;
+       mdev->active = false;
+       mutex_unlock(&mdev_list_lock);
 
        type = to_mdev_type(mdev->type_kobj);
        parent = mdev->parent;
-       mutex_lock(&parent->lock);
 
        ret = mdev_device_remove_ops(mdev, force_remove);
        if (ret) {
-               mutex_unlock(&parent->lock);
-
-               mutex_lock(&mdev_list_lock);
-               list_add(&mdev->next, &mdev_list);
-               mutex_unlock(&mdev_list_lock);
-
+               mdev->active = true;
                return ret;
        }
 
        mdev_remove_sysfs_files(dev, type);
        device_unregister(dev);
-       mutex_unlock(&parent->lock);
        mdev_put_parent(parent);
 
        return 0;