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); |