Commit aba800a3ffdffd9484d458344bfea8886e9062d2
Committed by
Anthony Liguori
1 parent
7055e687
qemu/virtio: MSI-X support in virtio PCI
This enables actual support for MSI-X in virtio PCI. First user will be virtio-net. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Showing
2 changed files
with
113 additions
and
41 deletions
hw/virtio-pci.c
| @@ -18,6 +18,7 @@ | @@ -18,6 +18,7 @@ | ||
| 18 | #include "virtio.h" | 18 | #include "virtio.h" |
| 19 | #include "pci.h" | 19 | #include "pci.h" |
| 20 | //#include "sysemu.h" | 20 | //#include "sysemu.h" |
| 21 | +#include "msix.h" | ||
| 21 | 22 | ||
| 22 | /* from Linux's linux/virtio_pci.h */ | 23 | /* from Linux's linux/virtio_pci.h */ |
| 23 | 24 | ||
| @@ -47,7 +48,24 @@ | @@ -47,7 +48,24 @@ | ||
| 47 | * a read-and-acknowledge. */ | 48 | * a read-and-acknowledge. */ |
| 48 | #define VIRTIO_PCI_ISR 19 | 49 | #define VIRTIO_PCI_ISR 19 |
| 49 | 50 | ||
| 50 | -#define VIRTIO_PCI_CONFIG 20 | 51 | +/* MSI-X registers: only enabled if MSI-X is enabled. */ |
| 52 | +/* A 16-bit vector for configuration changes. */ | ||
| 53 | +#define VIRTIO_MSI_CONFIG_VECTOR 20 | ||
| 54 | +/* A 16-bit vector for selected queue notifications. */ | ||
| 55 | +#define VIRTIO_MSI_QUEUE_VECTOR 22 | ||
| 56 | + | ||
| 57 | +/* Config space size */ | ||
| 58 | +#define VIRTIO_PCI_CONFIG_NOMSI 20 | ||
| 59 | +#define VIRTIO_PCI_CONFIG_MSI 24 | ||
| 60 | +#define VIRTIO_PCI_REGION_SIZE(dev) (msix_present(dev) ? \ | ||
| 61 | + VIRTIO_PCI_CONFIG_MSI : \ | ||
| 62 | + VIRTIO_PCI_CONFIG_NOMSI) | ||
| 63 | + | ||
| 64 | +/* The remaining space is defined by each driver as the per-driver | ||
| 65 | + * configuration space */ | ||
| 66 | +#define VIRTIO_PCI_CONFIG(dev) (msix_enabled(dev) ? \ | ||
| 67 | + VIRTIO_PCI_CONFIG_MSI : \ | ||
| 68 | + VIRTIO_PCI_CONFIG_NOMSI) | ||
| 51 | 69 | ||
| 52 | /* Virtio ABI version, if we increment this, we break the guest driver. */ | 70 | /* Virtio ABI version, if we increment this, we break the guest driver. */ |
| 53 | #define VIRTIO_PCI_ABI_VERSION 0 | 71 | #define VIRTIO_PCI_ABI_VERSION 0 |
| @@ -81,14 +99,17 @@ typedef struct { | @@ -81,14 +99,17 @@ typedef struct { | ||
| 81 | static void virtio_pci_notify(void *opaque, uint16_t vector) | 99 | static void virtio_pci_notify(void *opaque, uint16_t vector) |
| 82 | { | 100 | { |
| 83 | VirtIOPCIProxy *proxy = opaque; | 101 | VirtIOPCIProxy *proxy = opaque; |
| 84 | - | ||
| 85 | - qemu_set_irq(proxy->pci_dev.irq[0], proxy->vdev->isr & 1); | 102 | + if (msix_enabled(&proxy->pci_dev)) |
| 103 | + msix_notify(&proxy->pci_dev, vector); | ||
| 104 | + else | ||
| 105 | + qemu_set_irq(proxy->pci_dev.irq[0], proxy->vdev->isr & 1); | ||
| 86 | } | 106 | } |
| 87 | 107 | ||
| 88 | static void virtio_pci_reset(void *opaque) | 108 | static void virtio_pci_reset(void *opaque) |
| 89 | { | 109 | { |
| 90 | VirtIOPCIProxy *proxy = opaque; | 110 | VirtIOPCIProxy *proxy = opaque; |
| 91 | virtio_reset(proxy->vdev); | 111 | virtio_reset(proxy->vdev); |
| 112 | + msix_reset(&proxy->pci_dev); | ||
| 92 | } | 113 | } |
| 93 | 114 | ||
| 94 | static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) | 115 | static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) |
| @@ -97,8 +118,6 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) | @@ -97,8 +118,6 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) | ||
| 97 | VirtIODevice *vdev = proxy->vdev; | 118 | VirtIODevice *vdev = proxy->vdev; |
| 98 | target_phys_addr_t pa; | 119 | target_phys_addr_t pa; |
| 99 | 120 | ||
| 100 | - addr -= proxy->addr; | ||
| 101 | - | ||
| 102 | switch (addr) { | 121 | switch (addr) { |
| 103 | case VIRTIO_PCI_GUEST_FEATURES: | 122 | case VIRTIO_PCI_GUEST_FEATURES: |
| 104 | /* Guest does not negotiate properly? We have to assume nothing. */ | 123 | /* Guest does not negotiate properly? We have to assume nothing. */ |
| @@ -131,17 +150,33 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) | @@ -131,17 +150,33 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) | ||
| 131 | if (vdev->status == 0) | 150 | if (vdev->status == 0) |
| 132 | virtio_pci_reset(proxy); | 151 | virtio_pci_reset(proxy); |
| 133 | break; | 152 | break; |
| 153 | + case VIRTIO_MSI_CONFIG_VECTOR: | ||
| 154 | + msix_vector_unuse(&proxy->pci_dev, vdev->config_vector); | ||
| 155 | + /* Make it possible for guest to discover an error took place. */ | ||
| 156 | + if (msix_vector_use(&proxy->pci_dev, val) < 0) | ||
| 157 | + val = VIRTIO_NO_VECTOR; | ||
| 158 | + vdev->config_vector = val; | ||
| 159 | + break; | ||
| 160 | + case VIRTIO_MSI_QUEUE_VECTOR: | ||
| 161 | + msix_vector_unuse(&proxy->pci_dev, | ||
| 162 | + virtio_queue_vector(vdev, vdev->queue_sel)); | ||
| 163 | + /* Make it possible for guest to discover an error took place. */ | ||
| 164 | + if (msix_vector_use(&proxy->pci_dev, val) < 0) | ||
| 165 | + val = VIRTIO_NO_VECTOR; | ||
| 166 | + virtio_queue_set_vector(vdev, vdev->queue_sel, val); | ||
| 167 | + break; | ||
| 168 | + default: | ||
| 169 | + fprintf(stderr, "%s: unexpected address 0x%x value 0x%x\n", | ||
| 170 | + __func__, addr, val); | ||
| 171 | + break; | ||
| 134 | } | 172 | } |
| 135 | } | 173 | } |
| 136 | 174 | ||
| 137 | -static uint32_t virtio_ioport_read(void *opaque, uint32_t addr) | 175 | +static uint32_t virtio_ioport_read(VirtIOPCIProxy *proxy, uint32_t addr) |
| 138 | { | 176 | { |
| 139 | - VirtIOPCIProxy *proxy = opaque; | ||
| 140 | VirtIODevice *vdev = proxy->vdev; | 177 | VirtIODevice *vdev = proxy->vdev; |
| 141 | uint32_t ret = 0xFFFFFFFF; | 178 | uint32_t ret = 0xFFFFFFFF; |
| 142 | 179 | ||
| 143 | - addr -= proxy->addr; | ||
| 144 | - | ||
| 145 | switch (addr) { | 180 | switch (addr) { |
| 146 | case VIRTIO_PCI_HOST_FEATURES: | 181 | case VIRTIO_PCI_HOST_FEATURES: |
| 147 | ret = vdev->get_features(vdev); | 182 | ret = vdev->get_features(vdev); |
| @@ -171,6 +206,12 @@ static uint32_t virtio_ioport_read(void *opaque, uint32_t addr) | @@ -171,6 +206,12 @@ static uint32_t virtio_ioport_read(void *opaque, uint32_t addr) | ||
| 171 | vdev->isr = 0; | 206 | vdev->isr = 0; |
| 172 | qemu_set_irq(proxy->pci_dev.irq[0], 0); | 207 | qemu_set_irq(proxy->pci_dev.irq[0], 0); |
| 173 | break; | 208 | break; |
| 209 | + case VIRTIO_MSI_CONFIG_VECTOR: | ||
| 210 | + ret = vdev->config_vector; | ||
| 211 | + break; | ||
| 212 | + case VIRTIO_MSI_QUEUE_VECTOR: | ||
| 213 | + ret = virtio_queue_vector(vdev, vdev->queue_sel); | ||
| 214 | + break; | ||
| 174 | default: | 215 | default: |
| 175 | break; | 216 | break; |
| 176 | } | 217 | } |
| @@ -181,42 +222,72 @@ static uint32_t virtio_ioport_read(void *opaque, uint32_t addr) | @@ -181,42 +222,72 @@ static uint32_t virtio_ioport_read(void *opaque, uint32_t addr) | ||
| 181 | static uint32_t virtio_pci_config_readb(void *opaque, uint32_t addr) | 222 | static uint32_t virtio_pci_config_readb(void *opaque, uint32_t addr) |
| 182 | { | 223 | { |
| 183 | VirtIOPCIProxy *proxy = opaque; | 224 | VirtIOPCIProxy *proxy = opaque; |
| 184 | - addr -= proxy->addr + VIRTIO_PCI_CONFIG; | 225 | + uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev); |
| 226 | + addr -= proxy->addr; | ||
| 227 | + if (addr < config) | ||
| 228 | + return virtio_ioport_read(proxy, addr); | ||
| 229 | + addr -= config; | ||
| 185 | return virtio_config_readb(proxy->vdev, addr); | 230 | return virtio_config_readb(proxy->vdev, addr); |
| 186 | } | 231 | } |
| 187 | 232 | ||
| 188 | static uint32_t virtio_pci_config_readw(void *opaque, uint32_t addr) | 233 | static uint32_t virtio_pci_config_readw(void *opaque, uint32_t addr) |
| 189 | { | 234 | { |
| 190 | VirtIOPCIProxy *proxy = opaque; | 235 | VirtIOPCIProxy *proxy = opaque; |
| 191 | - addr -= proxy->addr + VIRTIO_PCI_CONFIG; | 236 | + uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev); |
| 237 | + addr -= proxy->addr; | ||
| 238 | + if (addr < config) | ||
| 239 | + return virtio_ioport_read(proxy, addr); | ||
| 240 | + addr -= config; | ||
| 192 | return virtio_config_readw(proxy->vdev, addr); | 241 | return virtio_config_readw(proxy->vdev, addr); |
| 193 | } | 242 | } |
| 194 | 243 | ||
| 195 | static uint32_t virtio_pci_config_readl(void *opaque, uint32_t addr) | 244 | static uint32_t virtio_pci_config_readl(void *opaque, uint32_t addr) |
| 196 | { | 245 | { |
| 197 | VirtIOPCIProxy *proxy = opaque; | 246 | VirtIOPCIProxy *proxy = opaque; |
| 198 | - addr -= proxy->addr + VIRTIO_PCI_CONFIG; | 247 | + uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev); |
| 248 | + addr -= proxy->addr; | ||
| 249 | + if (addr < config) | ||
| 250 | + return virtio_ioport_read(proxy, addr); | ||
| 251 | + addr -= config; | ||
| 199 | return virtio_config_readl(proxy->vdev, addr); | 252 | return virtio_config_readl(proxy->vdev, addr); |
| 200 | } | 253 | } |
| 201 | 254 | ||
| 202 | static void virtio_pci_config_writeb(void *opaque, uint32_t addr, uint32_t val) | 255 | static void virtio_pci_config_writeb(void *opaque, uint32_t addr, uint32_t val) |
| 203 | { | 256 | { |
| 204 | VirtIOPCIProxy *proxy = opaque; | 257 | VirtIOPCIProxy *proxy = opaque; |
| 205 | - addr -= proxy->addr + VIRTIO_PCI_CONFIG; | 258 | + uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev); |
| 259 | + addr -= proxy->addr; | ||
| 260 | + if (addr < config) { | ||
| 261 | + virtio_ioport_write(proxy, addr, val); | ||
| 262 | + return; | ||
| 263 | + } | ||
| 264 | + addr -= config; | ||
| 206 | virtio_config_writeb(proxy->vdev, addr, val); | 265 | virtio_config_writeb(proxy->vdev, addr, val); |
| 207 | } | 266 | } |
| 208 | 267 | ||
| 209 | static void virtio_pci_config_writew(void *opaque, uint32_t addr, uint32_t val) | 268 | static void virtio_pci_config_writew(void *opaque, uint32_t addr, uint32_t val) |
| 210 | { | 269 | { |
| 211 | VirtIOPCIProxy *proxy = opaque; | 270 | VirtIOPCIProxy *proxy = opaque; |
| 212 | - addr -= proxy->addr + VIRTIO_PCI_CONFIG; | 271 | + uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev); |
| 272 | + addr -= proxy->addr; | ||
| 273 | + if (addr < config) { | ||
| 274 | + virtio_ioport_write(proxy, addr, val); | ||
| 275 | + return; | ||
| 276 | + } | ||
| 277 | + addr -= config; | ||
| 213 | virtio_config_writew(proxy->vdev, addr, val); | 278 | virtio_config_writew(proxy->vdev, addr, val); |
| 214 | } | 279 | } |
| 215 | 280 | ||
| 216 | static void virtio_pci_config_writel(void *opaque, uint32_t addr, uint32_t val) | 281 | static void virtio_pci_config_writel(void *opaque, uint32_t addr, uint32_t val) |
| 217 | { | 282 | { |
| 218 | VirtIOPCIProxy *proxy = opaque; | 283 | VirtIOPCIProxy *proxy = opaque; |
| 219 | - addr -= proxy->addr + VIRTIO_PCI_CONFIG; | 284 | + uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev); |
| 285 | + addr -= proxy->addr; | ||
| 286 | + if (addr < config) { | ||
| 287 | + virtio_ioport_write(proxy, addr, val); | ||
| 288 | + return; | ||
| 289 | + } | ||
| 290 | + addr -= config; | ||
| 220 | virtio_config_writel(proxy->vdev, addr, val); | 291 | virtio_config_writel(proxy->vdev, addr, val); |
| 221 | } | 292 | } |
| 222 | 293 | ||
| @@ -225,32 +296,26 @@ static void virtio_map(PCIDevice *pci_dev, int region_num, | @@ -225,32 +296,26 @@ static void virtio_map(PCIDevice *pci_dev, int region_num, | ||
| 225 | { | 296 | { |
| 226 | VirtIOPCIProxy *proxy = container_of(pci_dev, VirtIOPCIProxy, pci_dev); | 297 | VirtIOPCIProxy *proxy = container_of(pci_dev, VirtIOPCIProxy, pci_dev); |
| 227 | VirtIODevice *vdev = proxy->vdev; | 298 | VirtIODevice *vdev = proxy->vdev; |
| 228 | - int i; | 299 | + unsigned config_len = VIRTIO_PCI_REGION_SIZE(pci_dev) + vdev->config_len; |
| 229 | 300 | ||
| 230 | proxy->addr = addr; | 301 | proxy->addr = addr; |
| 231 | - for (i = 0; i < 3; i++) { | ||
| 232 | - register_ioport_write(addr, VIRTIO_PCI_CONFIG, 1 << i, | ||
| 233 | - virtio_ioport_write, proxy); | ||
| 234 | - register_ioport_read(addr, VIRTIO_PCI_CONFIG, 1 << i, | ||
| 235 | - virtio_ioport_read, proxy); | ||
| 236 | - } | ||
| 237 | 302 | ||
| 238 | - if (vdev->config_len) { | ||
| 239 | - register_ioport_write(addr + VIRTIO_PCI_CONFIG, vdev->config_len, 1, | ||
| 240 | - virtio_pci_config_writeb, proxy); | ||
| 241 | - register_ioport_write(addr + VIRTIO_PCI_CONFIG, vdev->config_len, 2, | ||
| 242 | - virtio_pci_config_writew, proxy); | ||
| 243 | - register_ioport_write(addr + VIRTIO_PCI_CONFIG, vdev->config_len, 4, | ||
| 244 | - virtio_pci_config_writel, proxy); | ||
| 245 | - register_ioport_read(addr + VIRTIO_PCI_CONFIG, vdev->config_len, 1, | ||
| 246 | - virtio_pci_config_readb, proxy); | ||
| 247 | - register_ioport_read(addr + VIRTIO_PCI_CONFIG, vdev->config_len, 2, | ||
| 248 | - virtio_pci_config_readw, proxy); | ||
| 249 | - register_ioport_read(addr + VIRTIO_PCI_CONFIG, vdev->config_len, 4, | ||
| 250 | - virtio_pci_config_readl, proxy); | 303 | + register_ioport_write(addr, config_len, 1, virtio_pci_config_writeb, proxy); |
| 304 | + register_ioport_write(addr, config_len, 2, virtio_pci_config_writew, proxy); | ||
| 305 | + register_ioport_write(addr, config_len, 4, virtio_pci_config_writel, proxy); | ||
| 306 | + register_ioport_read(addr, config_len, 1, virtio_pci_config_readb, proxy); | ||
| 307 | + register_ioport_read(addr, config_len, 2, virtio_pci_config_readw, proxy); | ||
| 308 | + register_ioport_read(addr, config_len, 4, virtio_pci_config_readl, proxy); | ||
| 251 | 309 | ||
| 310 | + if (vdev->config_len) | ||
| 252 | vdev->get_config(vdev, vdev->config); | 311 | vdev->get_config(vdev, vdev->config); |
| 253 | - } | 312 | +} |
| 313 | + | ||
| 314 | +static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, | ||
| 315 | + uint32_t val, int len) | ||
| 316 | +{ | ||
| 317 | + pci_default_write_config(pci_dev, address, val, len); | ||
| 318 | + msix_write_config(pci_dev, address, val, len); | ||
| 254 | } | 319 | } |
| 255 | 320 | ||
| 256 | static const VirtIOBindings virtio_pci_bindings = { | 321 | static const VirtIOBindings virtio_pci_bindings = { |
| @@ -266,9 +331,6 @@ static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev, | @@ -266,9 +331,6 @@ static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev, | ||
| 266 | 331 | ||
| 267 | proxy->vdev = vdev; | 332 | proxy->vdev = vdev; |
| 268 | 333 | ||
| 269 | - /* No support for multiple vectors yet. */ | ||
| 270 | - proxy->vdev->nvectors = 0; | ||
| 271 | - | ||
| 272 | config = proxy->pci_dev.config; | 334 | config = proxy->pci_dev.config; |
| 273 | pci_config_set_vendor_id(config, vendor); | 335 | pci_config_set_vendor_id(config, vendor); |
| 274 | pci_config_set_device_id(config, device); | 336 | pci_config_set_device_id(config, device); |
| @@ -286,7 +348,17 @@ static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev, | @@ -286,7 +348,17 @@ static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev, | ||
| 286 | 348 | ||
| 287 | config[0x3d] = 1; | 349 | config[0x3d] = 1; |
| 288 | 350 | ||
| 289 | - size = 20 + vdev->config_len; | 351 | + if (vdev->nvectors && !msix_init(&proxy->pci_dev, vdev->nvectors, 1, 0)) { |
| 352 | + pci_register_bar(&proxy->pci_dev, 1, | ||
| 353 | + msix_bar_size(&proxy->pci_dev), | ||
| 354 | + PCI_ADDRESS_SPACE_MEM, | ||
| 355 | + msix_mmio_map); | ||
| 356 | + proxy->pci_dev.config_write = virtio_write_config; | ||
| 357 | + proxy->pci_dev.unregister = msix_uninit; | ||
| 358 | + } else | ||
| 359 | + vdev->nvectors = 0; | ||
| 360 | + | ||
| 361 | + size = VIRTIO_PCI_REGION_SIZE(&proxy->pci_dev) + vdev->config_len; | ||
| 290 | if (size & (size-1)) | 362 | if (size & (size-1)) |
| 291 | size = 1 << qemu_fls(size); | 363 | size = 1 << qemu_fls(size); |
| 292 | 364 |
rules.mak
| 1 | 1 | ||
| 2 | %.o: %.c | 2 | %.o: %.c |
| 3 | - $(call quiet-command,$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<," CC $(TARGET_DIR)$@") | 3 | + $(call quiet-command,$(CC) $(CPPFLAGS) $(CFLAGS) -Werror -Wno-error=uninitialized -c -o $@ $<," CC $(TARGET_DIR)$@") |
| 4 | 4 | ||
| 5 | %.o: %.S | 5 | %.o: %.S |
| 6 | $(call quiet-command,$(CC) $(CPPFLAGS) -c -o $@ $<," AS $(TARGET_DIR)$@") | 6 | $(call quiet-command,$(CC) $(CPPFLAGS) -c -o $@ $<," AS $(TARGET_DIR)$@") |