Commit aba800a3ffdffd9484d458344bfea8886e9062d2

Authored by Michael S. Tsirkin
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)$@")