aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc Zyngier <maz@kernel.org>2021-08-25 18:19:27 +0100
committerMarc Zyngier <maz@kernel.org>2021-08-25 18:33:39 +0100
commitc247b4892fd1b05f845b5dfeb914ff2c06f64e64 (patch)
treeade2d170ccdba1d3bc323121e266b63b57f2c4db
parent25c1dc6c4942ff0949c08780fcad6b324fec6bf7 (diff)
downloadkvmtool-msix-fixes.tar.gz
virtio/pci: Size the MSI-X bar according to the number of MSI-Xmsix-fixes
Since 45d3b59e8c45 ("kvm tools: Increase amount of possible interrupts per PCI device"), the number of MSI-S has gone from 4 to 33. However, the corresponding storage hasn't been upgraded, and writing to the MSI-X table is a pretty risky business. Now that the Linux kernel writes to *all* MSI-X entries before doing anything else with the device, kvmtool dies a horrible death. Fix it by properly defining the size of the MSI-X bar, and make Linux great again. Signed-off-by: Marc Zyngier <maz@kernel.org>
-rw-r--r--virtio/pci.c29
1 files changed, 21 insertions, 8 deletions
diff --git a/virtio/pci.c b/virtio/pci.c
index eb91f512..726146fc 100644
--- a/virtio/pci.c
+++ b/virtio/pci.c
@@ -7,6 +7,7 @@
#include "kvm/irq.h"
#include "kvm/virtio.h"
#include "kvm/ioeventfd.h"
+#include "kvm/util.h"
#include <sys/ioctl.h>
#include <linux/virtio_pci.h>
@@ -14,6 +15,13 @@
#include <assert.h>
#include <string.h>
+#define ALIGN_UP(x, s) ALIGN((x) + (s) - 1, (s))
+#define VIRTIO_NR_MSIX (VIRTIO_PCI_MAX_VQ + VIRTIO_PCI_MAX_CONFIG)
+#define VIRTIO_MSIX_TABLE_SIZE (VIRTIO_NR_MSIX * 16)
+#define VIRTIO_MSIX_PBA_SIZE (ALIGN_UP(VIRTIO_MSIX_TABLE_SIZE, 64) / 8)
+#define VIRTIO_MSIX_BAR_SIZE (1UL << fls_long(VIRTIO_MSIX_TABLE_SIZE + \
+ VIRTIO_MSIX_PBA_SIZE))
+
static u16 virtio_pci__port_addr(struct virtio_pci *vpci)
{
return pci__bar_address(&vpci->pci_hdr, 0);
@@ -336,15 +344,20 @@ static void virtio_pci__msix_mmio_callback(struct kvm_cpu *vcpu,
int vecnum;
size_t offset;
- if (addr > msix_io_addr + PCI_IO_SIZE) {
+ if (addr > msix_io_addr + VIRTIO_MSIX_TABLE_SIZE) {
+ /* Read access to PBA */
if (is_write)
return;
- table = (struct msix_table *)&vpci->msix_pba;
- offset = addr - (msix_io_addr + PCI_IO_SIZE);
- } else {
- table = vpci->msix_table;
- offset = addr - msix_io_addr;
+ offset = addr - (msix_io_addr + VIRTIO_MSIX_TABLE_SIZE);
+ if ((offset + len) > sizeof (vpci->msix_pba))
+ return;
+ memcpy(data, (void *)&vpci->msix_pba + offset, len);
+ return;
}
+
+ table = vpci->msix_table;
+ offset = addr - msix_io_addr;
+
vecnum = offset / sizeof(struct msix_table);
offset = offset % sizeof(struct msix_table);
@@ -520,7 +533,7 @@ int virtio_pci__init(struct kvm *kvm, void *dev, struct virtio_device *vdev,
port_addr = pci_get_io_port_block(PCI_IO_SIZE);
mmio_addr = pci_get_mmio_block(PCI_IO_SIZE);
- msix_io_block = pci_get_mmio_block(PCI_IO_SIZE * 2);
+ msix_io_block = pci_get_mmio_block(VIRTIO_MSIX_BAR_SIZE);
vpci->pci_hdr = (struct pci_device_header) {
.vendor_id = cpu_to_le16(PCI_VENDOR_ID_REDHAT_QUMRANET),
@@ -543,7 +556,7 @@ int virtio_pci__init(struct kvm *kvm, void *dev, struct virtio_device *vdev,
.capabilities = (void *)&vpci->pci_hdr.msix - (void *)&vpci->pci_hdr,
.bar_size[0] = cpu_to_le32(PCI_IO_SIZE),
.bar_size[1] = cpu_to_le32(PCI_IO_SIZE),
- .bar_size[2] = cpu_to_le32(PCI_IO_SIZE*2),
+ .bar_size[2] = cpu_to_le32(VIRTIO_MSIX_BAR_SIZE),
};
r = pci__register_bar_regions(kvm, &vpci->pci_hdr,