Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'vfio-v5.2-rc5' of git://github.com/awilliam/linux-vfio

Pull VFIO fixes from Alex Williamson:
"Fix mdev device create/remove paths to provide initialized device for
parent driver create callback and correct ordering of device removal
from bus prior to initiating removal by parent.

Also resolve races between parent removal and device create/remove
paths (all from Parav Pandit)"

* tag 'vfio-v5.2-rc5' of git://github.com/awilliam/linux-vfio:
vfio/mdev: Synchronize device create/remove with parent removal
vfio/mdev: Avoid creating sysfs remove file on stale device removal
vfio/mdev: Improve the create/remove sequence

+70 -78
+65 -73
drivers/vfio/mdev/mdev_core.c
··· 102 102 kref_put(&parent->ref, mdev_release_parent); 103 103 } 104 104 105 - static int mdev_device_create_ops(struct kobject *kobj, 106 - struct mdev_device *mdev) 105 + /* Caller must hold parent unreg_sem read or write lock */ 106 + static void mdev_device_remove_common(struct mdev_device *mdev) 107 107 { 108 - struct mdev_parent *parent = mdev->parent; 108 + struct mdev_parent *parent; 109 + struct mdev_type *type; 109 110 int ret; 110 111 111 - ret = parent->ops->create(kobj, mdev); 112 - if (ret) 113 - return ret; 114 - 115 - ret = sysfs_create_groups(&mdev->dev.kobj, 116 - parent->ops->mdev_attr_groups); 117 - if (ret) 118 - parent->ops->remove(mdev); 119 - 120 - return ret; 121 - } 122 - 123 - /* 124 - * mdev_device_remove_ops gets called from sysfs's 'remove' and when parent 125 - * device is being unregistered from mdev device framework. 126 - * - 'force_remove' is set to 'false' when called from sysfs's 'remove' which 127 - * indicates that if the mdev device is active, used by VMM or userspace 128 - * application, vendor driver could return error then don't remove the device. 129 - * - 'force_remove' is set to 'true' when called from mdev_unregister_device() 130 - * which indicate that parent device is being removed from mdev device 131 - * framework so remove mdev device forcefully. 132 - */ 133 - static int mdev_device_remove_ops(struct mdev_device *mdev, bool force_remove) 134 - { 135 - struct mdev_parent *parent = mdev->parent; 136 - int ret; 137 - 138 - /* 139 - * Vendor driver can return error if VMM or userspace application is 140 - * using this mdev device. 141 - */ 112 + type = to_mdev_type(mdev->type_kobj); 113 + mdev_remove_sysfs_files(&mdev->dev, type); 114 + device_del(&mdev->dev); 115 + parent = mdev->parent; 116 + lockdep_assert_held(&parent->unreg_sem); 142 117 ret = parent->ops->remove(mdev); 143 - if (ret && !force_remove) 144 - return ret; 118 + if (ret) 119 + dev_err(&mdev->dev, "Remove failed: err=%d\n", ret); 145 120 146 - sysfs_remove_groups(&mdev->dev.kobj, parent->ops->mdev_attr_groups); 147 - return 0; 121 + /* Balances with device_initialize() */ 122 + put_device(&mdev->dev); 123 + mdev_put_parent(parent); 148 124 } 149 125 150 126 static int mdev_device_remove_cb(struct device *dev, void *data) 151 127 { 152 - if (dev_is_mdev(dev)) 153 - mdev_device_remove(dev, true); 128 + if (dev_is_mdev(dev)) { 129 + struct mdev_device *mdev; 154 130 131 + mdev = to_mdev_device(dev); 132 + mdev_device_remove_common(mdev); 133 + } 155 134 return 0; 156 135 } 157 136 ··· 172 193 } 173 194 174 195 kref_init(&parent->ref); 196 + init_rwsem(&parent->unreg_sem); 175 197 176 198 parent->dev = dev; 177 199 parent->ops = ops; ··· 231 251 dev_info(dev, "MDEV: Unregistering\n"); 232 252 233 253 list_del(&parent->next); 254 + mutex_unlock(&parent_list_lock); 255 + 256 + down_write(&parent->unreg_sem); 257 + 234 258 class_compat_remove_link(mdev_bus_compat_class, dev, NULL); 235 259 236 260 device_for_each_child(dev, NULL, mdev_device_remove_cb); 237 261 238 262 parent_remove_sysfs_files(parent); 263 + up_write(&parent->unreg_sem); 239 264 240 - mutex_unlock(&parent_list_lock); 241 265 mdev_put_parent(parent); 242 266 } 243 267 EXPORT_SYMBOL(mdev_unregister_device); 244 268 245 - static void mdev_device_release(struct device *dev) 269 + static void mdev_device_free(struct mdev_device *mdev) 246 270 { 247 - struct mdev_device *mdev = to_mdev_device(dev); 248 - 249 271 mutex_lock(&mdev_list_lock); 250 272 list_del(&mdev->next); 251 273 mutex_unlock(&mdev_list_lock); 252 274 253 275 dev_dbg(&mdev->dev, "MDEV: destroying\n"); 254 276 kfree(mdev); 277 + } 278 + 279 + static void mdev_device_release(struct device *dev) 280 + { 281 + struct mdev_device *mdev = to_mdev_device(dev); 282 + 283 + mdev_device_free(mdev); 255 284 } 256 285 257 286 int mdev_device_create(struct kobject *kobj, ··· 299 310 300 311 mdev->parent = parent; 301 312 313 + /* Check if parent unregistration has started */ 314 + if (!down_read_trylock(&parent->unreg_sem)) { 315 + mdev_device_free(mdev); 316 + ret = -ENODEV; 317 + goto mdev_fail; 318 + } 319 + 320 + device_initialize(&mdev->dev); 302 321 mdev->dev.parent = dev; 303 322 mdev->dev.bus = &mdev_bus_type; 304 323 mdev->dev.release = mdev_device_release; 305 324 dev_set_name(&mdev->dev, "%pUl", uuid); 325 + mdev->dev.groups = parent->ops->mdev_attr_groups; 326 + mdev->type_kobj = kobj; 306 327 307 - ret = device_register(&mdev->dev); 308 - if (ret) { 309 - put_device(&mdev->dev); 310 - goto mdev_fail; 311 - } 312 - 313 - ret = mdev_device_create_ops(kobj, mdev); 328 + ret = parent->ops->create(kobj, mdev); 314 329 if (ret) 315 - goto create_fail; 330 + goto ops_create_fail; 331 + 332 + ret = device_add(&mdev->dev); 333 + if (ret) 334 + goto add_fail; 316 335 317 336 ret = mdev_create_sysfs_files(&mdev->dev, type); 318 - if (ret) { 319 - mdev_device_remove_ops(mdev, true); 320 - goto create_fail; 321 - } 337 + if (ret) 338 + goto sysfs_fail; 322 339 323 - mdev->type_kobj = kobj; 324 340 mdev->active = true; 325 341 dev_dbg(&mdev->dev, "MDEV: created\n"); 342 + up_read(&parent->unreg_sem); 326 343 327 344 return 0; 328 345 329 - create_fail: 330 - device_unregister(&mdev->dev); 346 + sysfs_fail: 347 + device_del(&mdev->dev); 348 + add_fail: 349 + parent->ops->remove(mdev); 350 + ops_create_fail: 351 + up_read(&parent->unreg_sem); 352 + put_device(&mdev->dev); 331 353 mdev_fail: 332 354 mdev_put_parent(parent); 333 355 return ret; 334 356 } 335 357 336 - int mdev_device_remove(struct device *dev, bool force_remove) 358 + int mdev_device_remove(struct device *dev) 337 359 { 338 360 struct mdev_device *mdev, *tmp; 339 361 struct mdev_parent *parent; 340 - struct mdev_type *type; 341 - int ret; 342 362 343 363 mdev = to_mdev_device(dev); 344 364 ··· 370 372 mdev->active = false; 371 373 mutex_unlock(&mdev_list_lock); 372 374 373 - type = to_mdev_type(mdev->type_kobj); 374 375 parent = mdev->parent; 376 + /* Check if parent unregistration has started */ 377 + if (!down_read_trylock(&parent->unreg_sem)) 378 + return -ENODEV; 375 379 376 - ret = mdev_device_remove_ops(mdev, force_remove); 377 - if (ret) { 378 - mdev->active = true; 379 - return ret; 380 - } 381 - 382 - mdev_remove_sysfs_files(dev, type); 383 - device_unregister(dev); 384 - mdev_put_parent(parent); 385 - 380 + mdev_device_remove_common(mdev); 381 + up_read(&parent->unreg_sem); 386 382 return 0; 387 383 } 388 384
+3 -1
drivers/vfio/mdev/mdev_private.h
··· 23 23 struct list_head next; 24 24 struct kset *mdev_types_kset; 25 25 struct list_head type_list; 26 + /* Synchronize device creation/removal with parent unregistration */ 27 + struct rw_semaphore unreg_sem; 26 28 }; 27 29 28 30 struct mdev_device { ··· 62 60 63 61 int mdev_device_create(struct kobject *kobj, 64 62 struct device *dev, const guid_t *uuid); 65 - int mdev_device_remove(struct device *dev, bool force_remove); 63 + int mdev_device_remove(struct device *dev); 66 64 67 65 #endif /* MDEV_PRIVATE_H */
+2 -4
drivers/vfio/mdev/mdev_sysfs.c
··· 236 236 if (val && device_remove_file_self(dev, attr)) { 237 237 int ret; 238 238 239 - ret = mdev_device_remove(dev, false); 240 - if (ret) { 241 - device_create_file(dev, attr); 239 + ret = mdev_device_remove(dev); 240 + if (ret) 242 241 return ret; 243 - } 244 242 } 245 243 246 244 return count;