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 | 18 | #include "virtio.h" |
| 19 | 19 | #include "pci.h" |
| 20 | 20 | //#include "sysemu.h" |
| 21 | +#include "msix.h" | |
| 21 | 22 | |
| 22 | 23 | /* from Linux's linux/virtio_pci.h */ |
| 23 | 24 | |
| ... | ... | @@ -47,7 +48,24 @@ |
| 47 | 48 | * a read-and-acknowledge. */ |
| 48 | 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 | 70 | /* Virtio ABI version, if we increment this, we break the guest driver. */ |
| 53 | 71 | #define VIRTIO_PCI_ABI_VERSION 0 |
| ... | ... | @@ -81,14 +99,17 @@ typedef struct { |
| 81 | 99 | static void virtio_pci_notify(void *opaque, uint16_t vector) |
| 82 | 100 | { |
| 83 | 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 | 108 | static void virtio_pci_reset(void *opaque) |
| 89 | 109 | { |
| 90 | 110 | VirtIOPCIProxy *proxy = opaque; |
| 91 | 111 | virtio_reset(proxy->vdev); |
| 112 | + msix_reset(&proxy->pci_dev); | |
| 92 | 113 | } |
| 93 | 114 | |
| 94 | 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 | 118 | VirtIODevice *vdev = proxy->vdev; |
| 98 | 119 | target_phys_addr_t pa; |
| 99 | 120 | |
| 100 | - addr -= proxy->addr; | |
| 101 | - | |
| 102 | 121 | switch (addr) { |
| 103 | 122 | case VIRTIO_PCI_GUEST_FEATURES: |
| 104 | 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 | 150 | if (vdev->status == 0) |
| 132 | 151 | virtio_pci_reset(proxy); |
| 133 | 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 | 177 | VirtIODevice *vdev = proxy->vdev; |
| 141 | 178 | uint32_t ret = 0xFFFFFFFF; |
| 142 | 179 | |
| 143 | - addr -= proxy->addr; | |
| 144 | - | |
| 145 | 180 | switch (addr) { |
| 146 | 181 | case VIRTIO_PCI_HOST_FEATURES: |
| 147 | 182 | ret = vdev->get_features(vdev); |
| ... | ... | @@ -171,6 +206,12 @@ static uint32_t virtio_ioport_read(void *opaque, uint32_t addr) |
| 171 | 206 | vdev->isr = 0; |
| 172 | 207 | qemu_set_irq(proxy->pci_dev.irq[0], 0); |
| 173 | 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 | 215 | default: |
| 175 | 216 | break; |
| 176 | 217 | } |
| ... | ... | @@ -181,42 +222,72 @@ static uint32_t virtio_ioport_read(void *opaque, uint32_t addr) |
| 181 | 222 | static uint32_t virtio_pci_config_readb(void *opaque, uint32_t addr) |
| 182 | 223 | { |
| 183 | 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 | 230 | return virtio_config_readb(proxy->vdev, addr); |
| 186 | 231 | } |
| 187 | 232 | |
| 188 | 233 | static uint32_t virtio_pci_config_readw(void *opaque, uint32_t addr) |
| 189 | 234 | { |
| 190 | 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 | 241 | return virtio_config_readw(proxy->vdev, addr); |
| 193 | 242 | } |
| 194 | 243 | |
| 195 | 244 | static uint32_t virtio_pci_config_readl(void *opaque, uint32_t addr) |
| 196 | 245 | { |
| 197 | 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 | 252 | return virtio_config_readl(proxy->vdev, addr); |
| 200 | 253 | } |
| 201 | 254 | |
| 202 | 255 | static void virtio_pci_config_writeb(void *opaque, uint32_t addr, uint32_t val) |
| 203 | 256 | { |
| 204 | 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 | 265 | virtio_config_writeb(proxy->vdev, addr, val); |
| 207 | 266 | } |
| 208 | 267 | |
| 209 | 268 | static void virtio_pci_config_writew(void *opaque, uint32_t addr, uint32_t val) |
| 210 | 269 | { |
| 211 | 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 | 278 | virtio_config_writew(proxy->vdev, addr, val); |
| 214 | 279 | } |
| 215 | 280 | |
| 216 | 281 | static void virtio_pci_config_writel(void *opaque, uint32_t addr, uint32_t val) |
| 217 | 282 | { |
| 218 | 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 | 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 | 296 | { |
| 226 | 297 | VirtIOPCIProxy *proxy = container_of(pci_dev, VirtIOPCIProxy, pci_dev); |
| 227 | 298 | VirtIODevice *vdev = proxy->vdev; |
| 228 | - int i; | |
| 299 | + unsigned config_len = VIRTIO_PCI_REGION_SIZE(pci_dev) + vdev->config_len; | |
| 229 | 300 | |
| 230 | 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 | 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 | 321 | static const VirtIOBindings virtio_pci_bindings = { |
| ... | ... | @@ -266,9 +331,6 @@ static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev, |
| 266 | 331 | |
| 267 | 332 | proxy->vdev = vdev; |
| 268 | 333 | |
| 269 | - /* No support for multiple vectors yet. */ | |
| 270 | - proxy->vdev->nvectors = 0; | |
| 271 | - | |
| 272 | 334 | config = proxy->pci_dev.config; |
| 273 | 335 | pci_config_set_vendor_id(config, vendor); |
| 274 | 336 | pci_config_set_device_id(config, device); |
| ... | ... | @@ -286,7 +348,17 @@ static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev, |
| 286 | 348 | |
| 287 | 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 | 362 | if (size & (size-1)) |
| 291 | 363 | size = 1 << qemu_fls(size); |
| 292 | 364 | ... | ... |
rules.mak
| 1 | 1 | |
| 2 | 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 | 5 | %.o: %.S |
| 6 | 6 | $(call quiet-command,$(CC) $(CPPFLAGS) -c -o $@ $<," AS $(TARGET_DIR)$@") | ... | ... |