From 45aa4e8e39bfc1fe0246e9c33ef30c9309f548c9 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 24 Mar 2016 15:07:59 +0200 Subject: pci-testdev: fast mmio support Teach PCI testdev to use fast MMIO when kvm makes it available. Before: mmio-wildcard-eventfd:pci-mem 2271 After: mmio-wildcard-eventfd:pci-mem 1218 Signed-off-by: Michael S. Tsirkin --- hw/misc/pci-testdev.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'hw') diff --git a/hw/misc/pci-testdev.c b/hw/misc/pci-testdev.c index 5df9089218..2f2e989778 100644 --- a/hw/misc/pci-testdev.c +++ b/hw/misc/pci-testdev.c @@ -239,6 +239,7 @@ static void pci_testdev_realize(PCIDevice *pci_dev, Error **errp) uint8_t *pci_conf; char *name; int r, i; + bool fastmmio = kvm_ioeventfd_any_length_enabled(); pci_conf = pci_dev->config; @@ -261,8 +262,12 @@ static void pci_testdev_realize(PCIDevice *pci_dev, Error **errp) memcpy(test->hdr->name, name, strlen(name) + 1); g_free(name); test->hdr->offset = cpu_to_le32(IOTEST_SIZE(i) + i * IOTEST_ACCESS_WIDTH); - test->size = IOTEST_ACCESS_WIDTH; test->match_data = strcmp(IOTEST_TEST(i), "wildcard-eventfd"); + if (fastmmio && IOTEST_IS_MEM(i) && !test->match_data) { + test->size = 0; + } else { + test->size = IOTEST_ACCESS_WIDTH; + } test->hdr->test = i; test->hdr->data = test->match_data ? IOTEST_DATAMATCH : IOTEST_NOMATCH; test->hdr->width = IOTEST_ACCESS_WIDTH; -- cgit v1.2.1 From 0f8445820f11a69154309863960328dda3dc1ad4 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Sun, 6 Mar 2016 20:44:30 +0200 Subject: xen: piix reuse pci generic class init function piix3_ide_xen_class_init is identical to piix3_ide_class_init except it's buggy as it does not set exit and does not disable hotplug properly. Switch to the generic one. Reviewed-by: Stefano Stabellini Signed-off-by: Michael S. Tsirkin --- hw/ide/piix.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'hw') diff --git a/hw/ide/piix.c b/hw/ide/piix.c index df46147c65..0a4cbcbcbb 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -258,22 +258,10 @@ static const TypeInfo piix3_ide_info = { .class_init = piix3_ide_class_init, }; -static void piix3_ide_xen_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = pci_piix_ide_realize; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82371SB_1; - k->class_id = PCI_CLASS_STORAGE_IDE; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - static const TypeInfo piix3_ide_xen_info = { .name = "piix3-ide-xen", .parent = TYPE_PCI_IDE, - .class_init = piix3_ide_xen_class_init, + .class_init = piix3_ide_class_init, }; static void piix4_ide_class_init(ObjectClass *klass, void *data) -- cgit v1.2.1 From bab47d9a75a33de747420e6b74fd157b708c890b Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 7 Apr 2016 09:12:58 -0500 Subject: Sort the fw_cfg file list Entries are inserted in filename order instead of being appended to the end in case sorting is enabled. This will avoid any future issues of moving the file creation around, it doesn't matter what order they are created now, the will always be in filename order. Signed-off-by: Gerd Hoffmann Added machine type handling for compatibility. This was a fairly complex change, this will preserve the order of fw_cfg for older versions no matter what order the firmware files actually come in. A list is kept of the correct legacy order and the entries will be inserted based upon their order in the list. Except that some entries are ordered (in a specific area of the list) based upon what order they appear on the command line. Special handling is added for those entries. Signed-off-by: Corey Minyard Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/core/loader.c | 14 ++++++ hw/i386/pc.c | 4 ++ hw/i386/pc_piix.c | 1 + hw/i386/pc_q35.c | 1 + hw/nvram/fw_cfg.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 5 files changed, 143 insertions(+), 8 deletions(-) (limited to 'hw') diff --git a/hw/core/loader.c b/hw/core/loader.c index 6b949fe44f..c0499571c3 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -1053,6 +1053,20 @@ void rom_set_fw(FWCfgState *f) fw_cfg = f; } +void rom_set_order_override(int order) +{ + if (!fw_cfg) + return; + fw_cfg_set_order_override(fw_cfg, order); +} + +void rom_reset_order_override(void) +{ + if (!fw_cfg) + return; + fw_cfg_reset_order_override(fw_cfg); +} + static Rom *find_rom(hwaddr addr) { Rom *rom; diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 2ac97c4f29..99437e0b78 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1406,6 +1406,7 @@ DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus) { DeviceState *dev = NULL; + rom_set_order_override(FW_CFG_ORDER_OVERRIDE_VGA); if (pci_bus) { PCIDevice *pcidev = pci_vga_init(pci_bus); dev = pcidev ? &pcidev->qdev : NULL; @@ -1413,6 +1414,7 @@ DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus) ISADevice *isadev = isa_vga_init(isa_bus); dev = isadev ? DEVICE(isadev) : NULL; } + rom_reset_order_override(); return dev; } @@ -1541,6 +1543,7 @@ void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus) { int i; + rom_set_order_override(FW_CFG_ORDER_OVERRIDE_NIC); for (i = 0; i < nb_nics; i++) { NICInfo *nd = &nd_table[i]; @@ -1550,6 +1553,7 @@ void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus) pci_nic_init_nofail(nd, pci_bus, "e1000", NULL); } } + rom_reset_order_override(); } void pc_pci_device_init(PCIBus *pci_bus) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 6a69b23abc..7f50116bc7 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -434,6 +434,7 @@ static void pc_i440fx_2_5_machine_options(MachineClass *m) m->alias = NULL; m->is_default = 0; pcmc->save_tsc_khz = false; + m->legacy_fw_cfg_order = 1; SET_MACHINE_COMPAT(m, PC_COMPAT_2_5); } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 9ee939b4c2..04aae8958c 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -298,6 +298,7 @@ static void pc_q35_2_5_machine_options(MachineClass *m) pc_q35_2_6_machine_options(m); m->alias = NULL; pcmc->save_tsc_khz = false; + m->legacy_fw_cfg_order = 1; SET_MACHINE_COMPAT(m, PC_COMPAT_2_5); } diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index d96932f6ca..999f480280 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -28,6 +28,7 @@ #include "hw/isa/isa.h" #include "hw/nvram/fw_cfg.h" #include "hw/sysbus.h" +#include "hw/boards.h" #include "trace.h" #include "qemu/error-report.h" #include "qemu/config-file.h" @@ -69,11 +70,14 @@ struct FWCfgState { /*< public >*/ FWCfgEntry entries[2][FW_CFG_MAX_ENTRY]; + int entry_order[FW_CFG_MAX_ENTRY]; FWCfgFiles *files; uint16_t cur_entry; uint32_t cur_offset; Notifier machine_ready; + int fw_cfg_order_override; + bool dma_enabled; dma_addr_t dma_addr; AddressSpace *dma_as; @@ -665,12 +669,87 @@ void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value) fw_cfg_add_bytes(s, key, copy, sizeof(value)); } +void fw_cfg_set_order_override(FWCfgState *s, int order) +{ + assert(s->fw_cfg_order_override == 0); + s->fw_cfg_order_override = order; +} + +void fw_cfg_reset_order_override(FWCfgState *s) +{ + assert(s->fw_cfg_order_override != 0); + s->fw_cfg_order_override = 0; +} + +/* + * This is the legacy order list. For legacy systems, files are in + * the fw_cfg in the order defined below, by the "order" value. Note + * that some entries (VGA ROMs, NIC option ROMS, etc.) go into a + * specific area, but there may be more than one and they occur in the + * order that the user specifies them on the command line. Those are + * handled in a special manner, using the order override above. + * + * For non-legacy, the files are sorted by filename to avoid this kind + * of complexity in the future. + * + * This is only for x86, other arches don't implement versioning so + * they won't set legacy mode. + */ +static struct { + const char *name; + int order; +} fw_cfg_order[] = { + { "etc/boot-menu-wait", 10 }, + { "bootsplash.jpg", 11 }, + { "bootsplash.bmp", 12 }, + { "etc/boot-fail-wait", 15 }, + { "etc/smbios/smbios-tables", 20 }, + { "etc/smbios/smbios-anchor", 30 }, + { "etc/e820", 40 }, + { "etc/reserved-memory-end", 50 }, + { "genroms/kvmvapic.bin", 55 }, + { "genroms/linuxboot.bin", 60 }, + { }, /* VGA ROMs from pc_vga_init come here, 70. */ + { }, /* NIC option ROMs from pc_nic_init come here, 80. */ + { "etc/system-states", 90 }, + { }, /* User ROMs come here, 100. */ + { }, /* Device FW comes here, 110. */ + { "etc/extra-pci-roots", 120 }, + { "etc/acpi/tables", 130 }, + { "etc/table-loader", 140 }, + { "etc/tpm/log", 150 }, + { "etc/acpi/rsdp", 160 }, + { "bootorder", 170 }, + +#define FW_CFG_ORDER_OVERRIDE_LAST 200 +}; + +static int get_fw_cfg_order(FWCfgState *s, const char *name) +{ + int i; + + if (s->fw_cfg_order_override > 0) + return s->fw_cfg_order_override; + + for (i = 0; i < ARRAY_SIZE(fw_cfg_order); i++) { + if (fw_cfg_order[i].name == NULL) + continue; + if (strcmp(name, fw_cfg_order[i].name) == 0) + return fw_cfg_order[i].order; + } + /* Stick unknown stuff at the end. */ + error_report("warning: Unknown firmware file in legacy mode: %s\n", name); + return FW_CFG_ORDER_OVERRIDE_LAST; +} + void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, FWCfgReadCallback callback, void *callback_opaque, void *data, size_t len) { - int i, index; + int i, index, count; size_t dsize; + MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); + int order = 0; if (!s->files) { dsize = sizeof(uint32_t) + sizeof(FWCfgFile) * FW_CFG_FILE_SLOTS; @@ -678,13 +757,48 @@ void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, fw_cfg_add_bytes(s, FW_CFG_FILE_DIR, s->files, dsize); } - index = be32_to_cpu(s->files->count); - assert(index < FW_CFG_FILE_SLOTS); + count = be32_to_cpu(s->files->count); + assert(count < FW_CFG_FILE_SLOTS); - pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name), - filename); - for (i = 0; i < index; i++) { - if (strcmp(s->files->f[index].name, s->files->f[i].name) == 0) { + /* Find the insertion point. */ + if (mc->legacy_fw_cfg_order) { + /* + * Sort by order. For files with the same order, we keep them + * in the sequence in which they were added. + */ + order = get_fw_cfg_order(s, filename); + for (index = count; + index > 0 && order < s->entry_order[index - 1]; + index--); + } else { + /* Sort by file name. */ + for (index = count; + index > 0 && strcmp(filename, s->files->f[index - 1].name) < 0; + index--); + } + + /* + * Move all the entries from the index point and after down one + * to create a slot for the new entry. Because calculations are + * being done with the index, make it so that "i" is the current + * index and "i - 1" is the one being copied from, thus the + * unusual start and end in the for statement. + */ + for (i = count + 1; i > index; i--) { + s->files->f[i] = s->files->f[i - 1]; + s->files->f[i].select = cpu_to_be16(FW_CFG_FILE_FIRST + i); + s->entries[0][FW_CFG_FILE_FIRST + i] = + s->entries[0][FW_CFG_FILE_FIRST + i - 1]; + s->entry_order[i] = s->entry_order[i - 1]; + } + + memset(&s->files->f[index], 0, sizeof(FWCfgFile)); + memset(&s->entries[0][FW_CFG_FILE_FIRST + index], 0, sizeof(FWCfgEntry)); + + pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name), filename); + for (i = 0; i <= count; i++) { + if (i != index && + strcmp(s->files->f[index].name, s->files->f[i].name) == 0) { error_report("duplicate fw_cfg file name: %s", s->files->f[index].name); exit(1); @@ -696,9 +810,10 @@ void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, s->files->f[index].size = cpu_to_be32(len); s->files->f[index].select = cpu_to_be16(FW_CFG_FILE_FIRST + index); + s->entry_order[index] = order; trace_fw_cfg_add_file(s, index, s->files->f[index].name, len); - s->files->count = cpu_to_be32(index+1); + s->files->count = cpu_to_be32(count+1); } void fw_cfg_add_file(FWCfgState *s, const char *filename, -- cgit v1.2.1 From 3d100d0fa9ee4826425ea1c2a120a0f661d8e739 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Mon, 4 Apr 2016 16:54:41 +0100 Subject: Migration: Add i82801b11 migration data The i82801b11 bridge didn't have a vmsd and thus didn't send any migration data, including that of its parent PCIBridge object. The symptom being if the guest used any devices behind the bridge the guest crashed (mostly with various interrupt related issues). Note: This will cause migration from old qemus that used this device to explicitly fail during migration as opposed to the guest crashing. Signed-off-by: Dr. David Alan Gilbert Suggested-by: Marcel Apfelbaum Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci-bridge/i82801b11.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'hw') diff --git a/hw/pci-bridge/i82801b11.c b/hw/pci-bridge/i82801b11.c index 5c40708ba8..2404e7ebae 100644 --- a/hw/pci-bridge/i82801b11.c +++ b/hw/pci-bridge/i82801b11.c @@ -78,6 +78,14 @@ err_bridge: return rc; } +static const VMStateDescription i82801b11_bridge_dev_vmstate = { + .name = "i82801b11_bridge", + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(parent_obj, PCIBridge), + VMSTATE_END_OF_LIST() + } +}; + static void i82801b11_bridge_class_init(ObjectClass *klass, void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -89,6 +97,7 @@ static void i82801b11_bridge_class_init(ObjectClass *klass, void *data) k->revision = ICH9_D2P_A2_REVISION; k->init = i82801b11_bridge_initfn; k->config_write = pci_bridge_write_config; + dc->vmsd = &i82801b11_bridge_dev_vmstate; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } -- cgit v1.2.1 From fecb48f7444edd29d2d6f048ce6460e1a23d0204 Mon Sep 17 00:00:00 2001 From: Pavel Butsykin Date: Tue, 29 Mar 2016 17:00:49 +0300 Subject: virtio-balloon: reset the statistic timer to load device If before loading snapshot we had set the timer of statistics, then after applying snapshot the expiry time would be irrelevant for the restored state of the virtual clocks. A simple fix is just to restart the timer after loading snapshot. For the user it may look like a long delay of statistics update after switch to the snapshot. Signed-off-by: Pavel Butsykin Reviewed-by: Roman Kagan Signed-off-by: Denis V. Lunev Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio-balloon.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'hw') diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index 22ad25cc97..c74101e479 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -426,6 +426,10 @@ static int virtio_balloon_load_device(VirtIODevice *vdev, QEMUFile *f, s->num_pages = qemu_get_be32(f); s->actual = qemu_get_be32(f); + + if (balloon_stats_enabled(s)) { + balloon_stats_change_timer(s, s->stats_poll_interval); + } return 0; } -- cgit v1.2.1 From 2b2cbcadc157295c7a1a25d7e7d0db9cdafcf773 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 Apr 2016 12:16:22 +0200 Subject: virtio: make virtio_queue_notify_vq static Acked-by: Cornelia Huck Signed-off-by: Paolo Bonzini Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'hw') diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 14d5d91397..264d4f6479 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -1088,7 +1088,7 @@ void virtio_queue_set_align(VirtIODevice *vdev, int n, int align) virtio_queue_update_rings(vdev, n); } -void virtio_queue_notify_vq(VirtQueue *vq) +static void virtio_queue_notify_vq(VirtQueue *vq) { if (vq->vring.desc && vq->handle_output) { VirtIODevice *vdev = vq->vdev; -- cgit v1.2.1 From eb41cf78fcdbe3ba3566482c72ff314246a906cc Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 Apr 2016 12:16:23 +0200 Subject: virtio-blk: fix disabled mode We must not call virtio_blk_data_plane_notify if dataplane is disabled: we would hit a segmentation fault in notify_guest_bh as s->guest_notifier has not been setup and is NULL. Reviewed-by: Cornelia Huck Signed-off-by: Paolo Bonzini Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/block/dataplane/virtio-blk.c | 7 +++---- hw/block/virtio-blk.c | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'hw') diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c index e666dd4ff0..2870d21a70 100644 --- a/hw/block/dataplane/virtio-blk.c +++ b/hw/block/dataplane/virtio-blk.c @@ -29,7 +29,6 @@ struct VirtIOBlockDataPlane { bool starting; bool stopping; - bool disabled; VirtIOBlkConf *conf; @@ -234,7 +233,7 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s) fail_host_notifier: k->set_guest_notifiers(qbus->parent, 1, false); fail_guest_notifiers: - s->disabled = true; + vblk->dataplane_disabled = true; s->starting = false; vblk->dataplane_started = true; } @@ -251,8 +250,8 @@ void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s) } /* Better luck next time. */ - if (s->disabled) { - s->disabled = false; + if (vblk->dataplane_disabled) { + vblk->dataplane_disabled = false; vblk->dataplane_started = false; return; } diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 870d345244..151fe788db 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -54,7 +54,7 @@ static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status) stb_p(&req->in->status, status); virtqueue_push(s->vq, &req->elem, req->in_len); - if (s->dataplane) { + if (s->dataplane_started && !s->dataplane_disabled) { virtio_blk_data_plane_notify(s->dataplane); } else { virtio_notify(vdev, s->vq); -- cgit v1.2.1 From 43c696a298f6bef81818b1d8e64d41a160782101 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 Apr 2016 12:16:24 +0200 Subject: virtio-scsi: fix disabled mode Add two missing checks for s->dataplane_fenced. In one case, QEMU would skip injecting an IRQ due to a write to an uninitialized EventNotifier's file descriptor. In the second case, the dataplane_disabled field was used by mistake; in fact after fixing this occurrence it is completely unused. Reviewed-by: Cornelia Huck Signed-off-by: Paolo Bonzini Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/scsi/virtio-scsi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'hw') diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index ade49727d6..38f1e2c2eb 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -68,7 +68,7 @@ static void virtio_scsi_complete_req(VirtIOSCSIReq *req) qemu_iovec_from_buf(&req->resp_iov, 0, &req->resp, req->resp_size); virtqueue_push(vq, &req->elem, req->qsgl.size + req->resp_iov.size); - if (s->dataplane_started) { + if (s->dataplane_started && !s->dataplane_fenced) { virtio_scsi_dataplane_notify(vdev, req); } else { virtio_notify(vdev, vq); @@ -773,7 +773,7 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, VirtIOSCSI *s = VIRTIO_SCSI(vdev); SCSIDevice *sd = SCSI_DEVICE(dev); - if (s->ctx && !s->dataplane_disabled) { + if (s->ctx && !s->dataplane_fenced) { VirtIOSCSIBlkChangeNotifier *insert_notifier, *remove_notifier; if (blk_op_is_blocked(sd->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) { -- cgit v1.2.1 From 344dc16fae0cb6a011aa5befffc8e7d520b11d5d Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 6 Apr 2016 12:16:25 +0200 Subject: virtio: add aio handler In addition to handling IO in vcpu thread and in io thread, blk dataplane introduces yet another mode: handling it by AioContext. Currently, this reuses the same handler as previous modes, which triggers races as these were not designed to be reentrant. Add instead a separate handler just for aio; this will make it possible to disable regular handlers when dataplane is active. Signed-off-by: Michael S. Tsirkin Signed-off-by: Paolo Bonzini Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio.c | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) (limited to 'hw') diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 264d4f6479..eb04ac07eb 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -96,6 +96,7 @@ struct VirtQueue uint16_t vector; void (*handle_output)(VirtIODevice *vdev, VirtQueue *vq); + void (*handle_aio_output)(VirtIODevice *vdev, VirtQueue *vq); VirtIODevice *vdev; EventNotifier guest_notifier; EventNotifier host_notifier; @@ -1088,6 +1089,16 @@ void virtio_queue_set_align(VirtIODevice *vdev, int n, int align) virtio_queue_update_rings(vdev, n); } +static void virtio_queue_notify_aio_vq(VirtQueue *vq) +{ + if (vq->vring.desc && vq->handle_aio_output) { + VirtIODevice *vdev = vq->vdev; + + trace_virtio_queue_notify(vdev, vq - vdev->vq, vq); + vq->handle_aio_output(vdev, vq); + } +} + static void virtio_queue_notify_vq(VirtQueue *vq) { if (vq->vring.desc && vq->handle_output) { @@ -1143,10 +1154,19 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size, vdev->vq[i].vring.num_default = queue_size; vdev->vq[i].vring.align = VIRTIO_PCI_VRING_ALIGN; vdev->vq[i].handle_output = handle_output; + vdev->vq[i].handle_aio_output = NULL; return &vdev->vq[i]; } +void virtio_set_queue_aio(VirtQueue *vq, + void (*handle_output)(VirtIODevice *, VirtQueue *)) +{ + assert(vq->handle_output); + + vq->handle_aio_output = handle_output; +} + void virtio_del_queue(VirtIODevice *vdev, int n) { if (n < 0 || n >= VIRTIO_QUEUE_MAX) { @@ -1780,11 +1800,11 @@ EventNotifier *virtio_queue_get_guest_notifier(VirtQueue *vq) return &vq->guest_notifier; } -static void virtio_queue_host_notifier_read(EventNotifier *n) +static void virtio_queue_host_notifier_aio_read(EventNotifier *n) { VirtQueue *vq = container_of(n, VirtQueue, host_notifier); if (event_notifier_test_and_clear(n)) { - virtio_queue_notify_vq(vq); + virtio_queue_notify_aio_vq(vq); } } @@ -1793,14 +1813,22 @@ void virtio_queue_aio_set_host_notifier_handler(VirtQueue *vq, AioContext *ctx, { if (assign && set_handler) { aio_set_event_notifier(ctx, &vq->host_notifier, true, - virtio_queue_host_notifier_read); + virtio_queue_host_notifier_aio_read); } else { aio_set_event_notifier(ctx, &vq->host_notifier, true, NULL); } if (!assign) { /* Test and clear notifier before after disabling event, * in case poll callback didn't have time to run. */ - virtio_queue_host_notifier_read(&vq->host_notifier); + virtio_queue_host_notifier_aio_read(&vq->host_notifier); + } +} + +static void virtio_queue_host_notifier_read(EventNotifier *n) +{ + VirtQueue *vq = container_of(n, VirtQueue, host_notifier); + if (event_notifier_test_and_clear(n)) { + virtio_queue_notify_vq(vq); } } -- cgit v1.2.1 From 8a2fad57eb124ec0633f6f2b1c74c991fc7501bd Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 6 Apr 2016 12:16:26 +0200 Subject: virtio-blk: use aio handler for data plane In addition to handling IO in vcpu thread and in io thread, dataplane introduces yet another mode: handling it by AioContext. This reuses the same handler as previous modes, which triggers races as these were not designed to be reentrant. Use a separate handler just for aio, and disable regular handlers when dataplane is active. Signed-off-by: Michael S. Tsirkin Signed-off-by: Paolo Bonzini Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/block/dataplane/virtio-blk.c | 13 +++++++++++++ hw/block/virtio-blk.c | 27 +++++++++++++++++---------- 2 files changed, 30 insertions(+), 10 deletions(-) (limited to 'hw') diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c index 2870d21a70..65c7f7041d 100644 --- a/hw/block/dataplane/virtio-blk.c +++ b/hw/block/dataplane/virtio-blk.c @@ -184,6 +184,17 @@ void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s) g_free(s); } +static void virtio_blk_data_plane_handle_output(VirtIODevice *vdev, + VirtQueue *vq) +{ + VirtIOBlock *s = (VirtIOBlock *)vdev; + + assert(s->dataplane); + assert(s->dataplane_started); + + virtio_blk_handle_vq(s, vq); +} + /* Context: QEMU global mutex held */ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s) { @@ -226,6 +237,7 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s) /* Get this show started by hooking up our callbacks */ aio_context_acquire(s->ctx); + virtio_set_queue_aio(s->vq, virtio_blk_data_plane_handle_output); virtio_queue_aio_set_host_notifier_handler(s->vq, s->ctx, true, true); aio_context_release(s->ctx); return; @@ -262,6 +274,7 @@ void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s) /* Stop notifications for new requests from guest */ virtio_queue_aio_set_host_notifier_handler(s->vq, s->ctx, false, false); + virtio_set_queue_aio(s->vq, NULL); /* Drain and switch bs back to the QEMU main loop */ blk_set_aio_context(s->conf->conf.blk, qemu_get_aio_context()); diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 151fe788db..3f88f8cf59 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -578,20 +578,11 @@ void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) } } -static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) +void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq) { - VirtIOBlock *s = VIRTIO_BLK(vdev); VirtIOBlockReq *req; MultiReqBuffer mrb = {}; - /* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start - * dataplane here instead of waiting for .set_status(). - */ - if (s->dataplane && !s->dataplane_started) { - virtio_blk_data_plane_start(s->dataplane); - return; - } - blk_io_plug(s->blk); while ((req = virtio_blk_get_request(s))) { @@ -605,6 +596,22 @@ static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) blk_io_unplug(s->blk); } +static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIOBlock *s = (VirtIOBlock *)vdev; + + if (s->dataplane) { + /* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start + * dataplane here instead of waiting for .set_status(). + */ + virtio_blk_data_plane_start(s->dataplane); + if (!s->dataplane_disabled) { + return; + } + } + virtio_blk_handle_vq(s, vq); +} + static void virtio_blk_dma_restart_bh(void *opaque) { VirtIOBlock *s = opaque; -- cgit v1.2.1 From a8f2e5c8fffbaf7fbd4f0efc8efbeebade78008f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 Apr 2016 12:16:27 +0200 Subject: virtio-scsi: use aio handler for data plane In addition to handling IO in vcpu thread and in io thread, dataplane introduces yet another mode: handling it by AioContext. This reuses the same handler as previous modes, which triggers races as these were not designed to be reentrant. Use a separate handler just for aio, and disable regular handlers when dataplane is active. Reviewed-by: Cornelia Huck Signed-off-by: Paolo Bonzini Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/scsi/virtio-scsi-dataplane.c | 43 ++++++++++++++++++++++++--- hw/scsi/virtio-scsi.c | 65 ++++++++++++++++++++++++++++------------- 2 files changed, 83 insertions(+), 25 deletions(-) (limited to 'hw') diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c index b44ac5dfa0..39ad086db7 100644 --- a/hw/scsi/virtio-scsi-dataplane.c +++ b/hw/scsi/virtio-scsi-dataplane.c @@ -38,7 +38,35 @@ void virtio_scsi_set_iothread(VirtIOSCSI *s, IOThread *iothread) } } -static int virtio_scsi_vring_init(VirtIOSCSI *s, VirtQueue *vq, int n) +static void virtio_scsi_data_plane_handle_cmd(VirtIODevice *vdev, + VirtQueue *vq) +{ + VirtIOSCSI *s = (VirtIOSCSI *)vdev; + + assert(s->ctx && s->dataplane_started); + virtio_scsi_handle_cmd_vq(s, vq); +} + +static void virtio_scsi_data_plane_handle_ctrl(VirtIODevice *vdev, + VirtQueue *vq) +{ + VirtIOSCSI *s = VIRTIO_SCSI(vdev); + + assert(s->ctx && s->dataplane_started); + virtio_scsi_handle_ctrl_vq(s, vq); +} + +static void virtio_scsi_data_plane_handle_event(VirtIODevice *vdev, + VirtQueue *vq) +{ + VirtIOSCSI *s = VIRTIO_SCSI(vdev); + + assert(s->ctx && s->dataplane_started); + virtio_scsi_handle_event_vq(s, vq); +} + +static int virtio_scsi_vring_init(VirtIOSCSI *s, VirtQueue *vq, int n, + void (*fn)(VirtIODevice *vdev, VirtQueue *vq)) { BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); @@ -54,6 +82,7 @@ static int virtio_scsi_vring_init(VirtIOSCSI *s, VirtQueue *vq, int n) } virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, true, true); + virtio_set_queue_aio(vq, fn); return 0; } @@ -71,9 +100,12 @@ static void virtio_scsi_clear_aio(VirtIOSCSI *s) int i; virtio_queue_aio_set_host_notifier_handler(vs->ctrl_vq, s->ctx, false, false); + virtio_set_queue_aio(vs->ctrl_vq, NULL); virtio_queue_aio_set_host_notifier_handler(vs->event_vq, s->ctx, false, false); + virtio_set_queue_aio(vs->event_vq, NULL); for (i = 0; i < vs->conf.num_queues; i++) { virtio_queue_aio_set_host_notifier_handler(vs->cmd_vqs[i], s->ctx, false, false); + virtio_set_queue_aio(vs->cmd_vqs[i], NULL); } } @@ -104,16 +136,19 @@ void virtio_scsi_dataplane_start(VirtIOSCSI *s) } aio_context_acquire(s->ctx); - rc = virtio_scsi_vring_init(s, vs->ctrl_vq, 0); + rc = virtio_scsi_vring_init(s, vs->ctrl_vq, 0, + virtio_scsi_data_plane_handle_ctrl); if (rc) { goto fail_vrings; } - rc = virtio_scsi_vring_init(s, vs->event_vq, 1); + rc = virtio_scsi_vring_init(s, vs->event_vq, 1, + virtio_scsi_data_plane_handle_event); if (rc) { goto fail_vrings; } for (i = 0; i < vs->conf.num_queues; i++) { - rc = virtio_scsi_vring_init(s, vs->cmd_vqs[i], i + 2); + rc = virtio_scsi_vring_init(s, vs->cmd_vqs[i], i + 2, + virtio_scsi_data_plane_handle_cmd); if (rc) { goto fail_vrings; } diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 38f1e2c2eb..30415c6a92 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -374,7 +374,7 @@ fail: return ret; } -void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req) +static void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req) { VirtIODevice *vdev = (VirtIODevice *)s; uint32_t type; @@ -412,20 +412,28 @@ void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req) } } -static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) +void virtio_scsi_handle_ctrl_vq(VirtIOSCSI *s, VirtQueue *vq) { - VirtIOSCSI *s = (VirtIOSCSI *)vdev; VirtIOSCSIReq *req; - if (s->ctx && !s->dataplane_started) { - virtio_scsi_dataplane_start(s); - return; - } while ((req = virtio_scsi_pop_req(s, vq))) { virtio_scsi_handle_ctrl_req(s, req); } } +static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIOSCSI *s = (VirtIOSCSI *)vdev; + + if (s->ctx) { + virtio_scsi_dataplane_start(s); + if (!s->dataplane_fenced) { + return; + } + } + virtio_scsi_handle_ctrl_vq(s, vq); +} + static void virtio_scsi_complete_cmd_req(VirtIOSCSIReq *req) { /* Sense data is not in req->resp and is copied separately @@ -508,7 +516,7 @@ static void virtio_scsi_fail_cmd_req(VirtIOSCSIReq *req) virtio_scsi_complete_cmd_req(req); } -bool virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req) +static bool virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req) { VirtIOSCSICommon *vs = &s->parent_obj; SCSIDevice *d; @@ -550,7 +558,7 @@ bool virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req) return true; } -void virtio_scsi_handle_cmd_req_submit(VirtIOSCSI *s, VirtIOSCSIReq *req) +static void virtio_scsi_handle_cmd_req_submit(VirtIOSCSI *s, VirtIOSCSIReq *req) { SCSIRequest *sreq = req->sreq; if (scsi_req_enqueue(sreq)) { @@ -560,17 +568,11 @@ void virtio_scsi_handle_cmd_req_submit(VirtIOSCSI *s, VirtIOSCSIReq *req) scsi_req_unref(sreq); } -static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq) +void virtio_scsi_handle_cmd_vq(VirtIOSCSI *s, VirtQueue *vq) { - /* use non-QOM casts in the data path */ - VirtIOSCSI *s = (VirtIOSCSI *)vdev; VirtIOSCSIReq *req, *next; QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs); - if (s->ctx && !s->dataplane_started) { - virtio_scsi_dataplane_start(s); - return; - } while ((req = virtio_scsi_pop_req(s, vq))) { if (virtio_scsi_handle_cmd_req_prepare(s, req)) { QTAILQ_INSERT_TAIL(&reqs, req, next); @@ -582,6 +584,20 @@ static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq) } } +static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq) +{ + /* use non-QOM casts in the data path */ + VirtIOSCSI *s = (VirtIOSCSI *)vdev; + + if (s->ctx) { + virtio_scsi_dataplane_start(s); + if (!s->dataplane_fenced) { + return; + } + } + virtio_scsi_handle_cmd_vq(s, vq); +} + static void virtio_scsi_get_config(VirtIODevice *vdev, uint8_t *config) { @@ -725,17 +741,24 @@ out: } } +void virtio_scsi_handle_event_vq(VirtIOSCSI *s, VirtQueue *vq) +{ + if (s->events_dropped) { + virtio_scsi_push_event(s, NULL, VIRTIO_SCSI_T_NO_EVENT, 0); + } +} + static void virtio_scsi_handle_event(VirtIODevice *vdev, VirtQueue *vq) { VirtIOSCSI *s = VIRTIO_SCSI(vdev); - if (s->ctx && !s->dataplane_started) { + if (s->ctx) { virtio_scsi_dataplane_start(s); - return; - } - if (s->events_dropped) { - virtio_scsi_push_event(s, NULL, VIRTIO_SCSI_T_NO_EVENT, 0); + if (!s->dataplane_fenced) { + return; + } } + virtio_scsi_handle_event_vq(s, vq); } static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense) -- cgit v1.2.1 From a378b49a4338ef61b86f4c74a1069036c7409141 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 Apr 2016 12:16:28 +0200 Subject: virtio: merge virtio_queue_aio_set_host_notifier_handler with virtio_queue_set_aio Eliminating the reentrancy is actually a nice thing that we can do with the API that Michael proposed, so let's make it first class. This also hides the complex assign/set_handler conventions from callers of virtio_queue_aio_set_host_notifier_handler, which in fact was always called with assign=true. Reviewed-by: Cornelia Huck Signed-off-by: Paolo Bonzini Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/block/dataplane/virtio-blk.c | 7 +++---- hw/scsi/virtio-scsi-dataplane.c | 12 ++++-------- hw/virtio/virtio.c | 17 +++++------------ 3 files changed, 12 insertions(+), 24 deletions(-) (limited to 'hw') diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c index 65c7f7041d..3cb97c9a29 100644 --- a/hw/block/dataplane/virtio-blk.c +++ b/hw/block/dataplane/virtio-blk.c @@ -237,8 +237,8 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s) /* Get this show started by hooking up our callbacks */ aio_context_acquire(s->ctx); - virtio_set_queue_aio(s->vq, virtio_blk_data_plane_handle_output); - virtio_queue_aio_set_host_notifier_handler(s->vq, s->ctx, true, true); + virtio_queue_aio_set_host_notifier_handler(s->vq, s->ctx, + virtio_blk_data_plane_handle_output); aio_context_release(s->ctx); return; @@ -273,8 +273,7 @@ void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s) aio_context_acquire(s->ctx); /* Stop notifications for new requests from guest */ - virtio_queue_aio_set_host_notifier_handler(s->vq, s->ctx, false, false); - virtio_set_queue_aio(s->vq, NULL); + virtio_queue_aio_set_host_notifier_handler(s->vq, s->ctx, NULL); /* Drain and switch bs back to the QEMU main loop */ blk_set_aio_context(s->conf->conf.blk, qemu_get_aio_context()); diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c index 39ad086db7..1a49f1e4b7 100644 --- a/hw/scsi/virtio-scsi-dataplane.c +++ b/hw/scsi/virtio-scsi-dataplane.c @@ -81,8 +81,7 @@ static int virtio_scsi_vring_init(VirtIOSCSI *s, VirtQueue *vq, int n, return rc; } - virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, true, true); - virtio_set_queue_aio(vq, fn); + virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, fn); return 0; } @@ -99,13 +98,10 @@ static void virtio_scsi_clear_aio(VirtIOSCSI *s) VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); int i; - virtio_queue_aio_set_host_notifier_handler(vs->ctrl_vq, s->ctx, false, false); - virtio_set_queue_aio(vs->ctrl_vq, NULL); - virtio_queue_aio_set_host_notifier_handler(vs->event_vq, s->ctx, false, false); - virtio_set_queue_aio(vs->event_vq, NULL); + virtio_queue_aio_set_host_notifier_handler(vs->ctrl_vq, s->ctx, NULL); + virtio_queue_aio_set_host_notifier_handler(vs->event_vq, s->ctx, NULL); for (i = 0; i < vs->conf.num_queues; i++) { - virtio_queue_aio_set_host_notifier_handler(vs->cmd_vqs[i], s->ctx, false, false); - virtio_set_queue_aio(vs->cmd_vqs[i], NULL); + virtio_queue_aio_set_host_notifier_handler(vs->cmd_vqs[i], s->ctx, NULL); } } diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index eb04ac07eb..f745c4abd0 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -1159,14 +1159,6 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size, return &vdev->vq[i]; } -void virtio_set_queue_aio(VirtQueue *vq, - void (*handle_output)(VirtIODevice *, VirtQueue *)) -{ - assert(vq->handle_output); - - vq->handle_aio_output = handle_output; -} - void virtio_del_queue(VirtIODevice *vdev, int n) { if (n < 0 || n >= VIRTIO_QUEUE_MAX) { @@ -1809,18 +1801,19 @@ static void virtio_queue_host_notifier_aio_read(EventNotifier *n) } void virtio_queue_aio_set_host_notifier_handler(VirtQueue *vq, AioContext *ctx, - bool assign, bool set_handler) + void (*handle_output)(VirtIODevice *, + VirtQueue *)) { - if (assign && set_handler) { + if (handle_output) { + vq->handle_aio_output = handle_output; aio_set_event_notifier(ctx, &vq->host_notifier, true, virtio_queue_host_notifier_aio_read); } else { aio_set_event_notifier(ctx, &vq->host_notifier, true, NULL); - } - if (!assign) { /* Test and clear notifier before after disabling event, * in case poll callback didn't have time to run. */ virtio_queue_host_notifier_aio_read(&vq->host_notifier); + vq->handle_aio_output = NULL; } } -- cgit v1.2.1 From 2e4278b534997d8f33a6e8b7923c168f5e5c89c7 Mon Sep 17 00:00:00 2001 From: Wei Jiangang Date: Wed, 23 Mar 2016 15:26:19 +0800 Subject: hw/pci-bridge: Add missing unref in case register-bus fails The error paths after a successful qdev_create/pci_bus_new should contain a object_unref/object_unparent. pxb_dev_init_common() did not yet, so add it. Signed-off-by: Wei Jiangang Signed-off-by: Cao jin Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Marcel Apfelbaum Reviewed-by: Markus Armbruster --- hw/pci-bridge/pci_expander_bridge.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'hw') diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c index 5e7e546b99..ba320bd857 100644 --- a/hw/pci-bridge/pci_expander_bridge.c +++ b/hw/pci-bridge/pci_expander_bridge.c @@ -249,7 +249,7 @@ static int pxb_dev_init_common(PCIDevice *dev, bool pcie) PCI_HOST_BRIDGE(ds)->bus = bus; if (pxb_register_bus(dev, bus)) { - return -EINVAL; + goto err_register_bus; } qdev_init_nofail(ds); @@ -263,6 +263,12 @@ static int pxb_dev_init_common(PCIDevice *dev, bool pcie) pxb_dev_list = g_list_insert_sorted(pxb_dev_list, pxb, pxb_compare); return 0; + +err_register_bus: + object_unref(OBJECT(bds)); + object_unparent(OBJECT(bus)); + object_unref(OBJECT(ds)); + return -EINVAL; } static int pxb_dev_initfn(PCIDevice *dev) -- cgit v1.2.1