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 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)$@")
... ...