| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Mediated device Core Driver |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. |
|---|
| 5 | 6 | * Author: Neo Jia <cjia@nvidia.com> |
|---|
| 6 | 7 | * 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 | 8 | */ |
|---|
| 12 | 9 | |
|---|
| 13 | 10 | #include <linux/module.h> |
|---|
| .. | .. |
|---|
| 60 | 57 | } |
|---|
| 61 | 58 | EXPORT_SYMBOL(mdev_from_dev); |
|---|
| 62 | 59 | |
|---|
| 63 | | -uuid_le mdev_uuid(struct mdev_device *mdev) |
|---|
| 60 | +const guid_t *mdev_uuid(struct mdev_device *mdev) |
|---|
| 64 | 61 | { |
|---|
| 65 | | - return mdev->uuid; |
|---|
| 62 | + return &mdev->uuid; |
|---|
| 66 | 63 | } |
|---|
| 67 | 64 | EXPORT_SYMBOL(mdev_uuid); |
|---|
| 68 | 65 | |
|---|
| .. | .. |
|---|
| 88 | 85 | put_device(dev); |
|---|
| 89 | 86 | } |
|---|
| 90 | 87 | |
|---|
| 91 | | -static |
|---|
| 92 | | -inline struct mdev_parent *mdev_get_parent(struct mdev_parent *parent) |
|---|
| 88 | +static struct mdev_parent *mdev_get_parent(struct mdev_parent *parent) |
|---|
| 93 | 89 | { |
|---|
| 94 | 90 | if (parent) |
|---|
| 95 | 91 | kref_get(&parent->ref); |
|---|
| .. | .. |
|---|
| 97 | 93 | return parent; |
|---|
| 98 | 94 | } |
|---|
| 99 | 95 | |
|---|
| 100 | | -static inline void mdev_put_parent(struct mdev_parent *parent) |
|---|
| 96 | +static void mdev_put_parent(struct mdev_parent *parent) |
|---|
| 101 | 97 | { |
|---|
| 102 | 98 | if (parent) |
|---|
| 103 | 99 | kref_put(&parent->ref, mdev_release_parent); |
|---|
| 104 | 100 | } |
|---|
| 105 | 101 | |
|---|
| 106 | | -static int mdev_device_create_ops(struct kobject *kobj, |
|---|
| 107 | | - struct mdev_device *mdev) |
|---|
| 102 | +/* Caller must hold parent unreg_sem read or write lock */ |
|---|
| 103 | +static void mdev_device_remove_common(struct mdev_device *mdev) |
|---|
| 108 | 104 | { |
|---|
| 109 | | - struct mdev_parent *parent = mdev->parent; |
|---|
| 105 | + struct mdev_parent *parent; |
|---|
| 106 | + struct mdev_type *type; |
|---|
| 110 | 107 | int ret; |
|---|
| 111 | 108 | |
|---|
| 112 | | - ret = parent->ops->create(kobj, mdev); |
|---|
| 113 | | - if (ret) |
|---|
| 114 | | - return ret; |
|---|
| 115 | | - |
|---|
| 116 | | - ret = sysfs_create_groups(&mdev->dev.kobj, |
|---|
| 117 | | - parent->ops->mdev_attr_groups); |
|---|
| 118 | | - if (ret) |
|---|
| 119 | | - parent->ops->remove(mdev); |
|---|
| 120 | | - |
|---|
| 121 | | - return ret; |
|---|
| 122 | | -} |
|---|
| 123 | | - |
|---|
| 124 | | -/* |
|---|
| 125 | | - * mdev_device_remove_ops gets called from sysfs's 'remove' and when parent |
|---|
| 126 | | - * device is being unregistered from mdev device framework. |
|---|
| 127 | | - * - 'force_remove' is set to 'false' when called from sysfs's 'remove' which |
|---|
| 128 | | - * indicates that if the mdev device is active, used by VMM or userspace |
|---|
| 129 | | - * application, vendor driver could return error then don't remove the device. |
|---|
| 130 | | - * - 'force_remove' is set to 'true' when called from mdev_unregister_device() |
|---|
| 131 | | - * which indicate that parent device is being removed from mdev device |
|---|
| 132 | | - * framework so remove mdev device forcefully. |
|---|
| 133 | | - */ |
|---|
| 134 | | -static int mdev_device_remove_ops(struct mdev_device *mdev, bool force_remove) |
|---|
| 135 | | -{ |
|---|
| 136 | | - struct mdev_parent *parent = mdev->parent; |
|---|
| 137 | | - int ret; |
|---|
| 138 | | - |
|---|
| 139 | | - /* |
|---|
| 140 | | - * Vendor driver can return error if VMM or userspace application is |
|---|
| 141 | | - * using this mdev device. |
|---|
| 142 | | - */ |
|---|
| 109 | + type = to_mdev_type(mdev->type_kobj); |
|---|
| 110 | + mdev_remove_sysfs_files(&mdev->dev, type); |
|---|
| 111 | + device_del(&mdev->dev); |
|---|
| 112 | + parent = mdev->parent; |
|---|
| 113 | + lockdep_assert_held(&parent->unreg_sem); |
|---|
| 143 | 114 | ret = parent->ops->remove(mdev); |
|---|
| 144 | | - if (ret && !force_remove) |
|---|
| 145 | | - return -EBUSY; |
|---|
| 115 | + if (ret) |
|---|
| 116 | + dev_err(&mdev->dev, "Remove failed: err=%d\n", ret); |
|---|
| 146 | 117 | |
|---|
| 147 | | - sysfs_remove_groups(&mdev->dev.kobj, parent->ops->mdev_attr_groups); |
|---|
| 148 | | - return 0; |
|---|
| 118 | + /* Balances with device_initialize() */ |
|---|
| 119 | + put_device(&mdev->dev); |
|---|
| 120 | + mdev_put_parent(parent); |
|---|
| 149 | 121 | } |
|---|
| 150 | 122 | |
|---|
| 151 | 123 | static int mdev_device_remove_cb(struct device *dev, void *data) |
|---|
| 152 | 124 | { |
|---|
| 153 | | - if (dev_is_mdev(dev)) |
|---|
| 154 | | - mdev_device_remove(dev, true); |
|---|
| 125 | + if (dev_is_mdev(dev)) { |
|---|
| 126 | + struct mdev_device *mdev; |
|---|
| 155 | 127 | |
|---|
| 128 | + mdev = to_mdev_device(dev); |
|---|
| 129 | + mdev_device_remove_common(mdev); |
|---|
| 130 | + } |
|---|
| 156 | 131 | return 0; |
|---|
| 157 | 132 | } |
|---|
| 158 | 133 | |
|---|
| .. | .. |
|---|
| 168 | 143 | { |
|---|
| 169 | 144 | int ret; |
|---|
| 170 | 145 | struct mdev_parent *parent; |
|---|
| 146 | + char *env_string = "MDEV_STATE=registered"; |
|---|
| 147 | + char *envp[] = { env_string, NULL }; |
|---|
| 171 | 148 | |
|---|
| 172 | 149 | /* check for mandatory ops */ |
|---|
| 173 | 150 | if (!ops || !ops->create || !ops->remove || !ops->supported_type_groups) |
|---|
| .. | .. |
|---|
| 194 | 171 | } |
|---|
| 195 | 172 | |
|---|
| 196 | 173 | kref_init(&parent->ref); |
|---|
| 174 | + init_rwsem(&parent->unreg_sem); |
|---|
| 197 | 175 | |
|---|
| 198 | 176 | parent->dev = dev; |
|---|
| 199 | 177 | parent->ops = ops; |
|---|
| .. | .. |
|---|
| 218 | 196 | mutex_unlock(&parent_list_lock); |
|---|
| 219 | 197 | |
|---|
| 220 | 198 | dev_info(dev, "MDEV: Registered\n"); |
|---|
| 199 | + kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); |
|---|
| 200 | + |
|---|
| 221 | 201 | return 0; |
|---|
| 222 | 202 | |
|---|
| 223 | 203 | add_dev_err: |
|---|
| .. | .. |
|---|
| 241 | 221 | void mdev_unregister_device(struct device *dev) |
|---|
| 242 | 222 | { |
|---|
| 243 | 223 | struct mdev_parent *parent; |
|---|
| 224 | + char *env_string = "MDEV_STATE=unregistered"; |
|---|
| 225 | + char *envp[] = { env_string, NULL }; |
|---|
| 244 | 226 | |
|---|
| 245 | 227 | mutex_lock(&parent_list_lock); |
|---|
| 246 | 228 | parent = __find_parent_device(dev); |
|---|
| .. | .. |
|---|
| 252 | 234 | dev_info(dev, "MDEV: Unregistering\n"); |
|---|
| 253 | 235 | |
|---|
| 254 | 236 | list_del(&parent->next); |
|---|
| 237 | + mutex_unlock(&parent_list_lock); |
|---|
| 238 | + |
|---|
| 239 | + down_write(&parent->unreg_sem); |
|---|
| 240 | + |
|---|
| 255 | 241 | class_compat_remove_link(mdev_bus_compat_class, dev, NULL); |
|---|
| 256 | 242 | |
|---|
| 257 | 243 | device_for_each_child(dev, NULL, mdev_device_remove_cb); |
|---|
| 258 | 244 | |
|---|
| 259 | 245 | parent_remove_sysfs_files(parent); |
|---|
| 246 | + up_write(&parent->unreg_sem); |
|---|
| 260 | 247 | |
|---|
| 261 | | - mutex_unlock(&parent_list_lock); |
|---|
| 262 | 248 | mdev_put_parent(parent); |
|---|
| 249 | + |
|---|
| 250 | + /* We still have the caller's reference to use for the uevent */ |
|---|
| 251 | + kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); |
|---|
| 263 | 252 | } |
|---|
| 264 | 253 | EXPORT_SYMBOL(mdev_unregister_device); |
|---|
| 265 | 254 | |
|---|
| 266 | | -static void mdev_device_release(struct device *dev) |
|---|
| 255 | +static void mdev_device_free(struct mdev_device *mdev) |
|---|
| 267 | 256 | { |
|---|
| 268 | | - struct mdev_device *mdev = to_mdev_device(dev); |
|---|
| 269 | | - |
|---|
| 270 | 257 | mutex_lock(&mdev_list_lock); |
|---|
| 271 | 258 | list_del(&mdev->next); |
|---|
| 272 | 259 | mutex_unlock(&mdev_list_lock); |
|---|
| .. | .. |
|---|
| 275 | 262 | kfree(mdev); |
|---|
| 276 | 263 | } |
|---|
| 277 | 264 | |
|---|
| 278 | | -int mdev_device_create(struct kobject *kobj, struct device *dev, uuid_le uuid) |
|---|
| 265 | +static void mdev_device_release(struct device *dev) |
|---|
| 266 | +{ |
|---|
| 267 | + struct mdev_device *mdev = to_mdev_device(dev); |
|---|
| 268 | + |
|---|
| 269 | + mdev_device_free(mdev); |
|---|
| 270 | +} |
|---|
| 271 | + |
|---|
| 272 | +int mdev_device_create(struct kobject *kobj, |
|---|
| 273 | + struct device *dev, const guid_t *uuid) |
|---|
| 279 | 274 | { |
|---|
| 280 | 275 | int ret; |
|---|
| 281 | 276 | struct mdev_device *mdev, *tmp; |
|---|
| .. | .. |
|---|
| 290 | 285 | |
|---|
| 291 | 286 | /* Check for duplicate */ |
|---|
| 292 | 287 | list_for_each_entry(tmp, &mdev_list, next) { |
|---|
| 293 | | - if (!uuid_le_cmp(tmp->uuid, uuid)) { |
|---|
| 288 | + if (guid_equal(&tmp->uuid, uuid)) { |
|---|
| 294 | 289 | mutex_unlock(&mdev_list_lock); |
|---|
| 295 | 290 | ret = -EEXIST; |
|---|
| 296 | 291 | goto mdev_fail; |
|---|
| .. | .. |
|---|
| 304 | 299 | goto mdev_fail; |
|---|
| 305 | 300 | } |
|---|
| 306 | 301 | |
|---|
| 307 | | - memcpy(&mdev->uuid, &uuid, sizeof(uuid_le)); |
|---|
| 302 | + guid_copy(&mdev->uuid, uuid); |
|---|
| 308 | 303 | list_add(&mdev->next, &mdev_list); |
|---|
| 309 | 304 | mutex_unlock(&mdev_list_lock); |
|---|
| 310 | 305 | |
|---|
| 311 | 306 | mdev->parent = parent; |
|---|
| 312 | | - kref_init(&mdev->ref); |
|---|
| 313 | 307 | |
|---|
| 314 | | - mdev->dev.parent = dev; |
|---|
| 315 | | - mdev->dev.bus = &mdev_bus_type; |
|---|
| 316 | | - mdev->dev.release = mdev_device_release; |
|---|
| 317 | | - dev_set_name(&mdev->dev, "%pUl", uuid.b); |
|---|
| 318 | | - |
|---|
| 319 | | - ret = device_register(&mdev->dev); |
|---|
| 320 | | - if (ret) { |
|---|
| 321 | | - put_device(&mdev->dev); |
|---|
| 308 | + /* Check if parent unregistration has started */ |
|---|
| 309 | + if (!down_read_trylock(&parent->unreg_sem)) { |
|---|
| 310 | + mdev_device_free(mdev); |
|---|
| 311 | + ret = -ENODEV; |
|---|
| 322 | 312 | goto mdev_fail; |
|---|
| 323 | 313 | } |
|---|
| 324 | 314 | |
|---|
| 325 | | - ret = mdev_device_create_ops(kobj, mdev); |
|---|
| 315 | + device_initialize(&mdev->dev); |
|---|
| 316 | + mdev->dev.parent = dev; |
|---|
| 317 | + mdev->dev.bus = &mdev_bus_type; |
|---|
| 318 | + mdev->dev.release = mdev_device_release; |
|---|
| 319 | + dev_set_name(&mdev->dev, "%pUl", uuid); |
|---|
| 320 | + mdev->dev.groups = parent->ops->mdev_attr_groups; |
|---|
| 321 | + mdev->type_kobj = kobj; |
|---|
| 322 | + |
|---|
| 323 | + ret = parent->ops->create(kobj, mdev); |
|---|
| 326 | 324 | if (ret) |
|---|
| 327 | | - goto create_fail; |
|---|
| 325 | + goto ops_create_fail; |
|---|
| 326 | + |
|---|
| 327 | + ret = device_add(&mdev->dev); |
|---|
| 328 | + if (ret) |
|---|
| 329 | + goto add_fail; |
|---|
| 328 | 330 | |
|---|
| 329 | 331 | ret = mdev_create_sysfs_files(&mdev->dev, type); |
|---|
| 330 | | - if (ret) { |
|---|
| 331 | | - mdev_device_remove_ops(mdev, true); |
|---|
| 332 | | - goto create_fail; |
|---|
| 333 | | - } |
|---|
| 332 | + if (ret) |
|---|
| 333 | + goto sysfs_fail; |
|---|
| 334 | 334 | |
|---|
| 335 | | - mdev->type_kobj = kobj; |
|---|
| 336 | 335 | mdev->active = true; |
|---|
| 337 | 336 | dev_dbg(&mdev->dev, "MDEV: created\n"); |
|---|
| 337 | + up_read(&parent->unreg_sem); |
|---|
| 338 | 338 | |
|---|
| 339 | 339 | return 0; |
|---|
| 340 | 340 | |
|---|
| 341 | | -create_fail: |
|---|
| 342 | | - device_unregister(&mdev->dev); |
|---|
| 341 | +sysfs_fail: |
|---|
| 342 | + device_del(&mdev->dev); |
|---|
| 343 | +add_fail: |
|---|
| 344 | + parent->ops->remove(mdev); |
|---|
| 345 | +ops_create_fail: |
|---|
| 346 | + up_read(&parent->unreg_sem); |
|---|
| 347 | + put_device(&mdev->dev); |
|---|
| 343 | 348 | mdev_fail: |
|---|
| 344 | 349 | mdev_put_parent(parent); |
|---|
| 345 | 350 | return ret; |
|---|
| 346 | 351 | } |
|---|
| 347 | 352 | |
|---|
| 348 | | -int mdev_device_remove(struct device *dev, bool force_remove) |
|---|
| 353 | +int mdev_device_remove(struct device *dev) |
|---|
| 349 | 354 | { |
|---|
| 350 | 355 | struct mdev_device *mdev, *tmp; |
|---|
| 351 | 356 | struct mdev_parent *parent; |
|---|
| 352 | | - struct mdev_type *type; |
|---|
| 353 | | - int ret; |
|---|
| 354 | 357 | |
|---|
| 355 | 358 | mdev = to_mdev_device(dev); |
|---|
| 356 | 359 | |
|---|
| .. | .. |
|---|
| 373 | 376 | mdev->active = false; |
|---|
| 374 | 377 | mutex_unlock(&mdev_list_lock); |
|---|
| 375 | 378 | |
|---|
| 376 | | - type = to_mdev_type(mdev->type_kobj); |
|---|
| 377 | 379 | parent = mdev->parent; |
|---|
| 380 | + /* Check if parent unregistration has started */ |
|---|
| 381 | + if (!down_read_trylock(&parent->unreg_sem)) |
|---|
| 382 | + return -ENODEV; |
|---|
| 378 | 383 | |
|---|
| 379 | | - ret = mdev_device_remove_ops(mdev, force_remove); |
|---|
| 380 | | - if (ret) { |
|---|
| 381 | | - mdev->active = true; |
|---|
| 382 | | - return ret; |
|---|
| 383 | | - } |
|---|
| 384 | + mdev_device_remove_common(mdev); |
|---|
| 385 | + up_read(&parent->unreg_sem); |
|---|
| 386 | + return 0; |
|---|
| 387 | +} |
|---|
| 384 | 388 | |
|---|
| 385 | | - mdev_remove_sysfs_files(dev, type); |
|---|
| 386 | | - device_unregister(dev); |
|---|
| 387 | | - mdev_put_parent(parent); |
|---|
| 389 | +int mdev_set_iommu_device(struct device *dev, struct device *iommu_device) |
|---|
| 390 | +{ |
|---|
| 391 | + struct mdev_device *mdev = to_mdev_device(dev); |
|---|
| 392 | + |
|---|
| 393 | + mdev->iommu_device = iommu_device; |
|---|
| 388 | 394 | |
|---|
| 389 | 395 | return 0; |
|---|
| 390 | 396 | } |
|---|
| 397 | +EXPORT_SYMBOL(mdev_set_iommu_device); |
|---|
| 398 | + |
|---|
| 399 | +struct device *mdev_get_iommu_device(struct device *dev) |
|---|
| 400 | +{ |
|---|
| 401 | + struct mdev_device *mdev = to_mdev_device(dev); |
|---|
| 402 | + |
|---|
| 403 | + return mdev->iommu_device; |
|---|
| 404 | +} |
|---|
| 405 | +EXPORT_SYMBOL(mdev_get_iommu_device); |
|---|
| 391 | 406 | |
|---|
| 392 | 407 | static int __init mdev_init(void) |
|---|
| 393 | 408 | { |
|---|