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