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

virtio_pci: harden MSI-X interrupts

We used to synchronize pending MSI-X irq handlers via
synchronize_irq(), this may not work for the untrusted device which
may keep sending interrupts after reset which may lead unexpected
results. Similarly, we should not enable MSI-X interrupt until the
device is ready. So this patch fixes those two issues by:

1) switching to use disable_irq() to prevent the virtio interrupt
handlers to be called after the device is reset.
2) using IRQF_NO_AUTOEN and enable the MSI-X irq during .ready()

This can make sure the virtio interrupt handler won't be called before
virtio_device_ready() and after reset.

Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Paul E. McKenney <paulmck@kernel.org>
Signed-off-by: Jason Wang <jasowang@redhat.com>
Link: https://lore.kernel.org/r/20211019070152.8236-5-jasowang@redhat.com
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>

authored by

Jason Wang and committed by
Michael S. Tsirkin
9e35276a d50497eb

+32 -12
+21 -6
drivers/virtio/virtio_pci_common.c
··· 24 24 "Force legacy mode for transitional virtio 1 devices"); 25 25 #endif 26 26 27 - /* wait for pending irq handlers */ 28 - void vp_synchronize_vectors(struct virtio_device *vdev) 27 + /* disable irq handlers */ 28 + void vp_disable_cbs(struct virtio_device *vdev) 29 29 { 30 30 struct virtio_pci_device *vp_dev = to_vp_device(vdev); 31 31 int i; ··· 34 34 synchronize_irq(vp_dev->pci_dev->irq); 35 35 36 36 for (i = 0; i < vp_dev->msix_vectors; ++i) 37 - synchronize_irq(pci_irq_vector(vp_dev->pci_dev, i)); 37 + disable_irq(pci_irq_vector(vp_dev->pci_dev, i)); 38 + } 39 + 40 + /* enable irq handlers */ 41 + void vp_enable_cbs(struct virtio_device *vdev) 42 + { 43 + struct virtio_pci_device *vp_dev = to_vp_device(vdev); 44 + int i; 45 + 46 + if (vp_dev->intx_enabled) 47 + return; 48 + 49 + for (i = 0; i < vp_dev->msix_vectors; ++i) 50 + enable_irq(pci_irq_vector(vp_dev->pci_dev, i)); 38 51 } 39 52 40 53 /* the notify function used when creating a virt queue */ ··· 154 141 snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names, 155 142 "%s-config", name); 156 143 err = request_irq(pci_irq_vector(vp_dev->pci_dev, v), 157 - vp_config_changed, 0, vp_dev->msix_names[v], 144 + vp_config_changed, IRQF_NO_AUTOEN, 145 + vp_dev->msix_names[v], 158 146 vp_dev); 159 147 if (err) 160 148 goto error; ··· 174 160 snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names, 175 161 "%s-virtqueues", name); 176 162 err = request_irq(pci_irq_vector(vp_dev->pci_dev, v), 177 - vp_vring_interrupt, 0, vp_dev->msix_names[v], 163 + vp_vring_interrupt, IRQF_NO_AUTOEN, 164 + vp_dev->msix_names[v], 178 165 vp_dev); 179 166 if (err) 180 167 goto error; ··· 352 337 "%s-%s", 353 338 dev_name(&vp_dev->vdev.dev), names[i]); 354 339 err = request_irq(pci_irq_vector(vp_dev->pci_dev, msix_vec), 355 - vring_interrupt, 0, 340 + vring_interrupt, IRQF_NO_AUTOEN, 356 341 vp_dev->msix_names[msix_vec], 357 342 vqs[i]); 358 343 if (err)
+4 -2
drivers/virtio/virtio_pci_common.h
··· 101 101 return container_of(vdev, struct virtio_pci_device, vdev); 102 102 } 103 103 104 - /* wait for pending irq handlers */ 105 - void vp_synchronize_vectors(struct virtio_device *vdev); 104 + /* disable irq handlers */ 105 + void vp_disable_cbs(struct virtio_device *vdev); 106 + /* enable irq handlers */ 107 + void vp_enable_cbs(struct virtio_device *vdev); 106 108 /* the notify function used when creating a virt queue */ 107 109 bool vp_notify(struct virtqueue *vq); 108 110 /* the config->del_vqs() implementation */
+3 -2
drivers/virtio/virtio_pci_legacy.c
··· 98 98 /* Flush out the status write, and flush in device writes, 99 99 * including MSi-X interrupts, if any. */ 100 100 vp_legacy_get_status(&vp_dev->ldev); 101 - /* Flush pending VQ/configuration callbacks. */ 102 - vp_synchronize_vectors(vdev); 101 + /* Disable VQ/configuration callbacks. */ 102 + vp_disable_cbs(vdev); 103 103 } 104 104 105 105 static u16 vp_config_vector(struct virtio_pci_device *vp_dev, u16 vector) ··· 185 185 } 186 186 187 187 static const struct virtio_config_ops virtio_pci_config_ops = { 188 + .enable_cbs = vp_enable_cbs, 188 189 .get = vp_get, 189 190 .set = vp_set, 190 191 .get_status = vp_get_status,
+4 -2
drivers/virtio/virtio_pci_modern.c
··· 172 172 */ 173 173 while (vp_modern_get_status(mdev)) 174 174 msleep(1); 175 - /* Flush pending VQ/configuration callbacks. */ 176 - vp_synchronize_vectors(vdev); 175 + /* Disable VQ/configuration callbacks. */ 176 + vp_disable_cbs(vdev); 177 177 } 178 178 179 179 static u16 vp_config_vector(struct virtio_pci_device *vp_dev, u16 vector) ··· 380 380 } 381 381 382 382 static const struct virtio_config_ops virtio_pci_config_nodev_ops = { 383 + .enable_cbs = vp_enable_cbs, 383 384 .get = NULL, 384 385 .set = NULL, 385 386 .generation = vp_generation, ··· 398 397 }; 399 398 400 399 static const struct virtio_config_ops virtio_pci_config_ops = { 400 + .enable_cbs = vp_enable_cbs, 401 401 .get = vp_get, 402 402 .set = vp_set, 403 403 .generation = vp_generation,