diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-21 00:30:12 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-21 00:30:12 -0500 |
| commit | 54e37b8dbe9a398f74cef313404bd2f1deca7853 (patch) | |
| tree | 2f72705eeb100b67b73902635763f08a6302a507 | |
| parent | 96680d2b9174668100824d763382240c71baa811 (diff) | |
| parent | 9a92c5091a42c565ede818fdf204c4f60004d0d8 (diff) | |
Merge tag 'vfio-for-v3.8-v2' of git://github.com/awilliam/linux-vfio
Pull vfio update from Alex Williamson.
* tag 'vfio-for-v3.8-v2' of git://github.com/awilliam/linux-vfio:
vfio-pci: Enable device before attempting reset
VFIO: fix out of order labels for error recovery in vfio_pci_init()
VFIO: use ACCESS_ONCE() to guard access to dev->driver
VFIO: unregister IOMMU notifier on error recovery path
vfio-pci: Re-order device reset
vfio: simplify kmalloc+copy_from_user to memdup_user
| -rw-r--r-- | drivers/vfio/pci/vfio_pci.c | 83 | ||||
| -rw-r--r-- | drivers/vfio/vfio.c | 34 |
2 files changed, 64 insertions, 53 deletions
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index 6c119944bbb6..b28e66c4376a 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c | |||
| @@ -43,6 +43,10 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev) | |||
| 43 | u16 cmd; | 43 | u16 cmd; |
| 44 | u8 msix_pos; | 44 | u8 msix_pos; |
| 45 | 45 | ||
| 46 | ret = pci_enable_device(pdev); | ||
| 47 | if (ret) | ||
| 48 | return ret; | ||
| 49 | |||
| 46 | vdev->reset_works = (pci_reset_function(pdev) == 0); | 50 | vdev->reset_works = (pci_reset_function(pdev) == 0); |
| 47 | pci_save_state(pdev); | 51 | pci_save_state(pdev); |
| 48 | vdev->pci_saved_state = pci_store_saved_state(pdev); | 52 | vdev->pci_saved_state = pci_store_saved_state(pdev); |
| @@ -51,8 +55,11 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev) | |||
| 51 | __func__, dev_name(&pdev->dev)); | 55 | __func__, dev_name(&pdev->dev)); |
| 52 | 56 | ||
| 53 | ret = vfio_config_init(vdev); | 57 | ret = vfio_config_init(vdev); |
| 54 | if (ret) | 58 | if (ret) { |
| 55 | goto out; | 59 | pci_load_and_free_saved_state(pdev, &vdev->pci_saved_state); |
| 60 | pci_disable_device(pdev); | ||
| 61 | return ret; | ||
| 62 | } | ||
| 56 | 63 | ||
| 57 | if (likely(!nointxmask)) | 64 | if (likely(!nointxmask)) |
| 58 | vdev->pci_2_3 = pci_intx_mask_supported(pdev); | 65 | vdev->pci_2_3 = pci_intx_mask_supported(pdev); |
| @@ -77,24 +84,15 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev) | |||
| 77 | } else | 84 | } else |
| 78 | vdev->msix_bar = 0xFF; | 85 | vdev->msix_bar = 0xFF; |
| 79 | 86 | ||
| 80 | ret = pci_enable_device(pdev); | 87 | return 0; |
| 81 | if (ret) | ||
| 82 | goto out; | ||
| 83 | |||
| 84 | return ret; | ||
| 85 | |||
| 86 | out: | ||
| 87 | kfree(vdev->pci_saved_state); | ||
| 88 | vdev->pci_saved_state = NULL; | ||
| 89 | vfio_config_free(vdev); | ||
| 90 | return ret; | ||
| 91 | } | 88 | } |
| 92 | 89 | ||
| 93 | static void vfio_pci_disable(struct vfio_pci_device *vdev) | 90 | static void vfio_pci_disable(struct vfio_pci_device *vdev) |
| 94 | { | 91 | { |
| 92 | struct pci_dev *pdev = vdev->pdev; | ||
| 95 | int bar; | 93 | int bar; |
| 96 | 94 | ||
| 97 | pci_disable_device(vdev->pdev); | 95 | pci_disable_device(pdev); |
| 98 | 96 | ||
| 99 | vfio_pci_set_irqs_ioctl(vdev, VFIO_IRQ_SET_DATA_NONE | | 97 | vfio_pci_set_irqs_ioctl(vdev, VFIO_IRQ_SET_DATA_NONE | |
| 100 | VFIO_IRQ_SET_ACTION_TRIGGER, | 98 | VFIO_IRQ_SET_ACTION_TRIGGER, |
| @@ -104,22 +102,40 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev) | |||
| 104 | 102 | ||
| 105 | vfio_config_free(vdev); | 103 | vfio_config_free(vdev); |
| 106 | 104 | ||
| 107 | pci_reset_function(vdev->pdev); | ||
| 108 | |||
| 109 | if (pci_load_and_free_saved_state(vdev->pdev, | ||
| 110 | &vdev->pci_saved_state) == 0) | ||
| 111 | pci_restore_state(vdev->pdev); | ||
| 112 | else | ||
| 113 | pr_info("%s: Couldn't reload %s saved state\n", | ||
| 114 | __func__, dev_name(&vdev->pdev->dev)); | ||
| 115 | |||
| 116 | for (bar = PCI_STD_RESOURCES; bar <= PCI_STD_RESOURCE_END; bar++) { | 105 | for (bar = PCI_STD_RESOURCES; bar <= PCI_STD_RESOURCE_END; bar++) { |
| 117 | if (!vdev->barmap[bar]) | 106 | if (!vdev->barmap[bar]) |
| 118 | continue; | 107 | continue; |
| 119 | pci_iounmap(vdev->pdev, vdev->barmap[bar]); | 108 | pci_iounmap(pdev, vdev->barmap[bar]); |
| 120 | pci_release_selected_regions(vdev->pdev, 1 << bar); | 109 | pci_release_selected_regions(pdev, 1 << bar); |
| 121 | vdev->barmap[bar] = NULL; | 110 | vdev->barmap[bar] = NULL; |
| 122 | } | 111 | } |
| 112 | |||
| 113 | /* | ||
| 114 | * If we have saved state, restore it. If we can reset the device, | ||
| 115 | * even better. Resetting with current state seems better than | ||
| 116 | * nothing, but saving and restoring current state without reset | ||
| 117 | * is just busy work. | ||
| 118 | */ | ||
| 119 | if (pci_load_and_free_saved_state(pdev, &vdev->pci_saved_state)) { | ||
| 120 | pr_info("%s: Couldn't reload %s saved state\n", | ||
| 121 | __func__, dev_name(&pdev->dev)); | ||
| 122 | |||
| 123 | if (!vdev->reset_works) | ||
| 124 | return; | ||
| 125 | |||
| 126 | pci_save_state(pdev); | ||
| 127 | } | ||
| 128 | |||
| 129 | /* | ||
| 130 | * Disable INTx and MSI, presumably to avoid spurious interrupts | ||
| 131 | * during reset. Stolen from pci_reset_function() | ||
| 132 | */ | ||
| 133 | pci_write_config_word(pdev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE); | ||
| 134 | |||
| 135 | if (vdev->reset_works) | ||
| 136 | __pci_reset_function(pdev); | ||
| 137 | |||
| 138 | pci_restore_state(pdev); | ||
| 123 | } | 139 | } |
| 124 | 140 | ||
| 125 | static void vfio_pci_release(void *device_data) | 141 | static void vfio_pci_release(void *device_data) |
| @@ -327,15 +343,10 @@ static long vfio_pci_ioctl(void *device_data, | |||
| 327 | hdr.count > vfio_pci_get_irq_count(vdev, hdr.index)) | 343 | hdr.count > vfio_pci_get_irq_count(vdev, hdr.index)) |
| 328 | return -EINVAL; | 344 | return -EINVAL; |
| 329 | 345 | ||
| 330 | data = kmalloc(hdr.count * size, GFP_KERNEL); | 346 | data = memdup_user((void __user *)(arg + minsz), |
| 331 | if (!data) | 347 | hdr.count * size); |
| 332 | return -ENOMEM; | 348 | if (IS_ERR(data)) |
| 333 | 349 | return PTR_ERR(data); | |
| 334 | if (copy_from_user(data, (void __user *)(arg + minsz), | ||
| 335 | hdr.count * size)) { | ||
| 336 | kfree(data); | ||
| 337 | return -EFAULT; | ||
| 338 | } | ||
| 339 | } | 350 | } |
| 340 | 351 | ||
| 341 | mutex_lock(&vdev->igate); | 352 | mutex_lock(&vdev->igate); |
| @@ -562,9 +573,9 @@ static int __init vfio_pci_init(void) | |||
| 562 | 573 | ||
| 563 | return 0; | 574 | return 0; |
| 564 | 575 | ||
| 565 | out_virqfd: | ||
| 566 | vfio_pci_virqfd_exit(); | ||
| 567 | out_driver: | 576 | out_driver: |
| 577 | vfio_pci_virqfd_exit(); | ||
| 578 | out_virqfd: | ||
| 568 | vfio_pci_uninit_perm_bits(); | 579 | vfio_pci_uninit_perm_bits(); |
| 569 | return ret; | 580 | return ret; |
| 570 | } | 581 | } |
diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index 56097c6d072d..12c264d3b058 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c | |||
| @@ -191,6 +191,17 @@ static void vfio_container_put(struct vfio_container *container) | |||
| 191 | kref_put(&container->kref, vfio_container_release); | 191 | kref_put(&container->kref, vfio_container_release); |
| 192 | } | 192 | } |
| 193 | 193 | ||
| 194 | static void vfio_group_unlock_and_free(struct vfio_group *group) | ||
| 195 | { | ||
| 196 | mutex_unlock(&vfio.group_lock); | ||
| 197 | /* | ||
| 198 | * Unregister outside of lock. A spurious callback is harmless now | ||
| 199 | * that the group is no longer in vfio.group_list. | ||
| 200 | */ | ||
| 201 | iommu_group_unregister_notifier(group->iommu_group, &group->nb); | ||
| 202 | kfree(group); | ||
| 203 | } | ||
| 204 | |||
| 194 | /** | 205 | /** |
| 195 | * Group objects - create, release, get, put, search | 206 | * Group objects - create, release, get, put, search |
| 196 | */ | 207 | */ |
| @@ -229,8 +240,7 @@ static struct vfio_group *vfio_create_group(struct iommu_group *iommu_group) | |||
| 229 | 240 | ||
| 230 | minor = vfio_alloc_group_minor(group); | 241 | minor = vfio_alloc_group_minor(group); |
| 231 | if (minor < 0) { | 242 | if (minor < 0) { |
| 232 | mutex_unlock(&vfio.group_lock); | 243 | vfio_group_unlock_and_free(group); |
| 233 | kfree(group); | ||
| 234 | return ERR_PTR(minor); | 244 | return ERR_PTR(minor); |
| 235 | } | 245 | } |
| 236 | 246 | ||
| @@ -239,8 +249,7 @@ static struct vfio_group *vfio_create_group(struct iommu_group *iommu_group) | |||
| 239 | if (tmp->iommu_group == iommu_group) { | 249 | if (tmp->iommu_group == iommu_group) { |
| 240 | vfio_group_get(tmp); | 250 | vfio_group_get(tmp); |
| 241 | vfio_free_group_minor(minor); | 251 | vfio_free_group_minor(minor); |
| 242 | mutex_unlock(&vfio.group_lock); | 252 | vfio_group_unlock_and_free(group); |
| 243 | kfree(group); | ||
| 244 | return tmp; | 253 | return tmp; |
| 245 | } | 254 | } |
| 246 | } | 255 | } |
| @@ -249,8 +258,7 @@ static struct vfio_group *vfio_create_group(struct iommu_group *iommu_group) | |||
| 249 | group, "%d", iommu_group_id(iommu_group)); | 258 | group, "%d", iommu_group_id(iommu_group)); |
| 250 | if (IS_ERR(dev)) { | 259 | if (IS_ERR(dev)) { |
| 251 | vfio_free_group_minor(minor); | 260 | vfio_free_group_minor(minor); |
| 252 | mutex_unlock(&vfio.group_lock); | 261 | vfio_group_unlock_and_free(group); |
| 253 | kfree(group); | ||
| 254 | return (struct vfio_group *)dev; /* ERR_PTR */ | 262 | return (struct vfio_group *)dev; /* ERR_PTR */ |
| 255 | } | 263 | } |
| 256 | 264 | ||
| @@ -274,16 +282,7 @@ static void vfio_group_release(struct kref *kref) | |||
| 274 | device_destroy(vfio.class, MKDEV(MAJOR(vfio.devt), group->minor)); | 282 | device_destroy(vfio.class, MKDEV(MAJOR(vfio.devt), group->minor)); |
| 275 | list_del(&group->vfio_next); | 283 | list_del(&group->vfio_next); |
| 276 | vfio_free_group_minor(group->minor); | 284 | vfio_free_group_minor(group->minor); |
| 277 | 285 | vfio_group_unlock_and_free(group); | |
| 278 | mutex_unlock(&vfio.group_lock); | ||
| 279 | |||
| 280 | /* | ||
| 281 | * Unregister outside of lock. A spurious callback is harmless now | ||
| 282 | * that the group is no longer in vfio.group_list. | ||
| 283 | */ | ||
| 284 | iommu_group_unregister_notifier(group->iommu_group, &group->nb); | ||
| 285 | |||
| 286 | kfree(group); | ||
| 287 | } | 286 | } |
| 288 | 287 | ||
| 289 | static void vfio_group_put(struct vfio_group *group) | 288 | static void vfio_group_put(struct vfio_group *group) |
| @@ -466,8 +465,9 @@ static int vfio_dev_viable(struct device *dev, void *data) | |||
| 466 | { | 465 | { |
| 467 | struct vfio_group *group = data; | 466 | struct vfio_group *group = data; |
| 468 | struct vfio_device *device; | 467 | struct vfio_device *device; |
| 468 | struct device_driver *drv = ACCESS_ONCE(dev->driver); | ||
| 469 | 469 | ||
| 470 | if (!dev->driver || vfio_whitelisted_driver(dev->driver)) | 470 | if (!drv || vfio_whitelisted_driver(drv)) |
| 471 | return 0; | 471 | return 0; |
| 472 | 472 | ||
| 473 | device = vfio_group_get_device(group, dev); | 473 | device = vfio_group_get_device(group, dev); |
