.. | .. |
---|
| 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 | { |
---|