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