diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2020-09-29 11:10:29 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2020-09-29 11:10:29 +0100 |
commit | 213057383c9f73a17cfe635b204d88e11f918df1 (patch) | |
tree | f3a1da40a8e48e3b8aaff8d355610e219c0de1cf /hw | |
parent | 23290e8070fa18dd8ff930515326e76eeee5810a (diff) | |
parent | 8138405528c29af2a850cd672a8f8a0b33b7ab40 (diff) | |
download | qemu-213057383c9f73a17cfe635b204d88e11f918df1.zip |
Merge remote-tracking branch 'remotes/mst/tags/for_upstream' into staging
virtio,pc,acpi: fixes, tests
Fixes and tests all over the place.
Batch iommu updates for vdpa.
Removal of deprecated cpu hotplug commands.
SMBIOS OEM string support.
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
# gpg: Signature made Tue 29 Sep 2020 08:09:21 BST
# gpg: using RSA key 5D09FD0871C8F85B94CA8A0D281F0DB8D28D5469
# gpg: issuer "mst@redhat.com"
# gpg: Good signature from "Michael S. Tsirkin <mst@kernel.org>" [full]
# gpg: aka "Michael S. Tsirkin <mst@redhat.com>" [full]
# Primary key fingerprint: 0270 606B 6F3C DF3D 0B17 0970 C350 3912 AFBE 8E67
# Subkey fingerprint: 5D09 FD08 71C8 F85B 94CA 8A0D 281F 0DB8 D28D 5469
* remotes/mst/tags/for_upstream: (48 commits)
libvhost-user: return on error in vu_log_queue_fill()
libvhost-user: return early on virtqueue errors
hw: virtio-pmem: detach the element fromt the virtqueue when error occurs
tests/acpi: update golden master DSDT binary table blobs for q35
piix4: don't reserve hw resources when hotplug is off globally
Add ACPI DSDT tables for q35 that are being updated by the next patch
tests/acpi: add newly added acpi DSDT table blob for pci bridge hotplug flag
tests/acpi: unit test for 'acpi-pci-hotplug-with-bridge-support' bridge flag
tests/acpi: list added acpi table binary file for pci bridge hotplug test
i440fx/acpi: do not add hotplug related amls for cold plugged bridges
Fix a gap where acpi_pcihp_find_hotplug_bus() returns a non-hotpluggable bus
tests/acpi: add a new ACPI table in order to test root pci hotplug on/off
tests/acpi: add new unit test to test hotplug off/on feature on the root pci bus
tests/acpi: mark addition of table DSDT.roothp for unit testing root pci hotplug
vhost-user: save features of multiqueues if chardev is closed
qemu-options: document SMBIOS type 11 settings
hw/smbios: report error if table size is too large
hw/smbios: support loading OEM strings values from a file
tests: acpi: update acpi blobs with new AML
x68: acpi: trigger SMI before sending hotplug Notify event to OSPM
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw')
32 files changed, 749 insertions, 169 deletions
diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index f6fbc9b95d..3792ba96ce 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -556,6 +556,15 @@ Aml *aml_or(Aml *arg1, Aml *arg2, Aml *dst) return build_opcode_2arg_dst(0x7D /* OrOp */, arg1, arg2, dst); } +/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLAnd */ +Aml *aml_land(Aml *arg1, Aml *arg2) +{ + Aml *var = aml_opcode(0x90 /* LAndOp */); + aml_append(var, arg1); + aml_append(var, arg2); + return var; +} + /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLOr */ Aml *aml_lor(Aml *arg1, Aml *arg2) { @@ -629,6 +638,13 @@ Aml *aml_notify(Aml *arg1, Aml *arg2) return var; } +/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefBreak */ +Aml *aml_break(void) +{ + Aml *var = aml_opcode(0xa5 /* BreakOp */); + return var; +} + /* helper to call method without argument */ Aml *aml_call0(const char *method) { diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c index 3d6a500fb7..8dd4d8ebbf 100644 --- a/hw/acpi/cpu.c +++ b/hw/acpi/cpu.c @@ -14,6 +14,8 @@ #define ACPI_CPU_CMD_DATA_OFFSET_RW 8 #define ACPI_CPU_CMD_DATA2_OFFSET_R 0 +#define OVMF_CPUHP_SMI_CMD 4 + enum { CPHP_GET_NEXT_CPU_WITH_EVENT_CMD = 0, CPHP_OST_EVENT_CMD = 1, @@ -321,6 +323,7 @@ const VMStateDescription vmstate_cpu_hotplug = { #define CPU_NOTIFY_METHOD "CTFY" #define CPU_EJECT_METHOD "CEJ0" #define CPU_OST_METHOD "COST" +#define CPU_ADDED_LIST "CNEW" #define CPU_ENABLED "CPEN" #define CPU_SELECTOR "CSEL" @@ -465,42 +468,150 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, method = aml_method(CPU_SCAN_METHOD, 0, AML_SERIALIZED); { + const uint8_t max_cpus_per_pass = 255; Aml *else_ctx; - Aml *while_ctx; + Aml *while_ctx, *while_ctx2; Aml *has_event = aml_local(0); Aml *dev_chk = aml_int(1); Aml *eject_req = aml_int(3); Aml *next_cpu_cmd = aml_int(CPHP_GET_NEXT_CPU_WITH_EVENT_CMD); + Aml *num_added_cpus = aml_local(1); + Aml *cpu_idx = aml_local(2); + Aml *uid = aml_local(3); + Aml *has_job = aml_local(4); + Aml *new_cpus = aml_name(CPU_ADDED_LIST); aml_append(method, aml_acquire(ctrl_lock, 0xFFFF)); - aml_append(method, aml_store(one, has_event)); - while_ctx = aml_while(aml_equal(has_event, one)); + + /* + * Windows versions newer than XP (including Windows 10/Windows + * Server 2019), do support* VarPackageOp but, it is cripled to hold + * the same elements number as old PackageOp. + * For compatibility with Windows XP (so it won't crash) use ACPI1.0 + * PackageOp which can hold max 255 elements. + * + * use named package as old Windows don't support it in local var + */ + aml_append(method, aml_name_decl(CPU_ADDED_LIST, + aml_package(max_cpus_per_pass))); + + aml_append(method, aml_store(zero, uid)); + aml_append(method, aml_store(one, has_job)); + /* + * CPU_ADDED_LIST can hold limited number of elements, outer loop + * allows to process CPUs in batches which let us to handle more + * CPUs than CPU_ADDED_LIST can hold. + */ + while_ctx2 = aml_while(aml_equal(has_job, one)); { - /* clear loop exit condition, ins_evt/rm_evt checks - * will set it to 1 while next_cpu_cmd returns a CPU - * with events */ - aml_append(while_ctx, aml_store(zero, has_event)); - aml_append(while_ctx, aml_store(next_cpu_cmd, cpu_cmd)); - ifctx = aml_if(aml_equal(ins_evt, one)); - { - aml_append(ifctx, - aml_call2(CPU_NOTIFY_METHOD, cpu_data, dev_chk)); - aml_append(ifctx, aml_store(one, ins_evt)); - aml_append(ifctx, aml_store(one, has_event)); - } - aml_append(while_ctx, ifctx); - else_ctx = aml_else(); - ifctx = aml_if(aml_equal(rm_evt, one)); - { - aml_append(ifctx, - aml_call2(CPU_NOTIFY_METHOD, cpu_data, eject_req)); - aml_append(ifctx, aml_store(one, rm_evt)); - aml_append(ifctx, aml_store(one, has_event)); - } - aml_append(else_ctx, ifctx); - aml_append(while_ctx, else_ctx); + aml_append(while_ctx2, aml_store(zero, has_job)); + + aml_append(while_ctx2, aml_store(one, has_event)); + aml_append(while_ctx2, aml_store(zero, num_added_cpus)); + + /* + * Scan CPUs, till there are CPUs with events or + * CPU_ADDED_LIST capacity is exhausted + */ + while_ctx = aml_while(aml_land(aml_equal(has_event, one), + aml_lless(uid, aml_int(arch_ids->len)))); + { + /* + * clear loop exit condition, ins_evt/rm_evt checks will + * set it to 1 while next_cpu_cmd returns a CPU with events + */ + aml_append(while_ctx, aml_store(zero, has_event)); + + aml_append(while_ctx, aml_store(uid, cpu_selector)); + aml_append(while_ctx, aml_store(next_cpu_cmd, cpu_cmd)); + + /* + * wrap around case, scan is complete, exit loop. + * It happens since events are not cleared in scan loop, + * so next_cpu_cmd continues to find already processed CPUs + */ + ifctx = aml_if(aml_lless(cpu_data, uid)); + { + aml_append(ifctx, aml_break()); + } + aml_append(while_ctx, ifctx); + + /* + * if CPU_ADDED_LIST is full, exit inner loop and process + * collected CPUs + */ + ifctx = aml_if( + aml_equal(num_added_cpus, aml_int(max_cpus_per_pass))); + { + aml_append(ifctx, aml_store(one, has_job)); + aml_append(ifctx, aml_break()); + } + aml_append(while_ctx, ifctx); + + aml_append(while_ctx, aml_store(cpu_data, uid)); + ifctx = aml_if(aml_equal(ins_evt, one)); + { + /* cache added CPUs to Notify/Wakeup later */ + aml_append(ifctx, aml_store(uid, + aml_index(new_cpus, num_added_cpus))); + aml_append(ifctx, aml_increment(num_added_cpus)); + aml_append(ifctx, aml_store(one, has_event)); + } + aml_append(while_ctx, ifctx); + else_ctx = aml_else(); + ifctx = aml_if(aml_equal(rm_evt, one)); + { + aml_append(ifctx, + aml_call2(CPU_NOTIFY_METHOD, uid, eject_req)); + aml_append(ifctx, aml_store(one, rm_evt)); + aml_append(ifctx, aml_store(one, has_event)); + } + aml_append(else_ctx, ifctx); + aml_append(while_ctx, else_ctx); + aml_append(while_ctx, aml_increment(uid)); + } + aml_append(while_ctx2, while_ctx); + + /* + * in case FW negotiated ICH9_LPC_SMI_F_CPU_HOTPLUG_BIT, + * make upcall to FW, so it can pull in new CPUs before + * OS is notified and wakes them up + */ + if (opts.smi_path) { + ifctx = aml_if(aml_lgreater(num_added_cpus, zero)); + { + aml_append(ifctx, aml_store(aml_int(OVMF_CPUHP_SMI_CMD), + aml_name("%s", opts.smi_path))); + } + aml_append(while_ctx2, ifctx); + } + + /* Notify OSPM about new CPUs and clear insert events */ + aml_append(while_ctx2, aml_store(zero, cpu_idx)); + while_ctx = aml_while(aml_lless(cpu_idx, num_added_cpus)); + { + aml_append(while_ctx, + aml_store(aml_derefof(aml_index(new_cpus, cpu_idx)), + uid)); + aml_append(while_ctx, + aml_call2(CPU_NOTIFY_METHOD, uid, dev_chk)); + aml_append(while_ctx, aml_store(uid, aml_debug())); + aml_append(while_ctx, aml_store(uid, cpu_selector)); + aml_append(while_ctx, aml_store(one, ins_evt)); + aml_append(while_ctx, aml_increment(cpu_idx)); + } + aml_append(while_ctx2, while_ctx); + /* + * If another batch is needed, then it will resume scanning + * exactly at -- and not after -- the last CPU that's currently + * in CPU_ADDED_LIST. In other words, the last CPU in + * CPU_ADDED_LIST is going to be re-checked. That's OK: we've + * just cleared the insert event for *all* CPUs in + * CPU_ADDED_LIST, including the last one. So the scan will + * simply seek past it. + */ } - aml_append(method, while_ctx); + aml_append(method, while_ctx2); aml_append(method, aml_release(ctrl_lock)); } aml_append(cpus_dev, method); diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index 6a19070cec..95cb0f935b 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -408,10 +408,20 @@ void ich9_pm_device_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev); if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) && - !lpc->pm.acpi_memory_hotplug.is_enabled) + !lpc->pm.acpi_memory_hotplug.is_enabled) { error_setg(errp, "memory hotplug is not enabled: %s.memory-hotplug-support " "is not set", object_get_typename(OBJECT(lpc))); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + uint64_t negotiated = lpc->smi_negotiated_features; + + if (negotiated & BIT_ULL(ICH9_LPC_SMI_F_BROADCAST_BIT) && + !(negotiated & BIT_ULL(ICH9_LPC_SMI_F_CPU_HOTPLUG_BIT))) { + error_setg(errp, "cpu hotplug with SMI wasn't enabled by firmware"); + error_append_hint(errp, "update machine type to newer than 5.1 " + "and firmware that suppors CPU hotplug with SMM"); + } + } } void ich9_pm_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, @@ -450,6 +460,18 @@ void ich9_pm_device_unplug_request_cb(HotplugHandler *hotplug_dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU) && !lpc->pm.cpu_hotplug_legacy) { + uint64_t negotiated = lpc->smi_negotiated_features; + + if (negotiated & BIT_ULL(ICH9_LPC_SMI_F_BROADCAST_BIT) && + !(negotiated & BIT_ULL(ICH9_LPC_SMI_F_CPU_HOT_UNPLUG_BIT))) { + error_setg(errp, "cpu hot-unplug with SMI wasn't enabled " + "by firmware"); + error_append_hint(errp, "update machine type to a version having " + "x-smi-cpu-hotunplug=on and firmware that " + "supports CPU hot-unplug with SMM"); + return; + } + acpi_cpu_unplug_request_cb(hotplug_dev, &lpc->pm.cpuhp_state, dev, errp); } else { diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index 39b1f74442..32ae8b2c0a 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -147,6 +147,21 @@ static PCIBus *acpi_pcihp_find_hotplug_bus(AcpiPciHpState *s, int bsel) if (!bsel && !find.bus) { find.bus = s->root; } + + /* + * Check if find.bus is actually hotpluggable. If bsel is set to + * NULL for example on the root bus in order to make it + * non-hotpluggable, find.bus will match the root bus when bsel + * is 0. See acpi_pcihp_test_hotplug_bus() above. Since the + * bus is not hotpluggable however, we should not select the bus. + * Instead, we should set find.bus to NULL in that case. In the check + * below, we generalize this case for all buses, not just the root bus. + * The callers of this function check for a null return value and + * handle them appropriately. + */ + if (find.bus && !qbus_is_hotpluggable(BUS(find.bus))) { + find.bus = NULL; + } return find.bus; } diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 832f8fba82..894d357f8c 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -596,8 +596,10 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, "acpi-gpe0", GPE_LEN); memory_region_add_subregion(parent, GPE_BASE, &s->io_gpe); - acpi_pcihp_init(OBJECT(s), &s->acpi_pci_hotplug, bus, parent, - s->use_acpi_hotplug_bridge); + if (s->use_acpi_hotplug_bridge || s->use_acpi_root_pci_hotplug) { + acpi_pcihp_init(OBJECT(s), &s->acpi_pci_hotplug, bus, parent, + s->use_acpi_hotplug_bridge); + } s->cpu_hotplug_legacy = true; object_property_add_bool(OBJECT(s), "cpu-hotplug-legacy", diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c index 39aec42dae..a076b1e54d 100644 --- a/hw/block/vhost-user-blk.c +++ b/hw/block/vhost-user-blk.c @@ -150,6 +150,7 @@ static int vhost_user_blk_start(VirtIODevice *vdev) error_report("Error starting vhost: %d", -ret); goto err_guest_notifiers; } + s->started_vu = true; /* guest_notifier_mask/pending not used yet, so just unmask * everything here. virtio-pci will do the right thing by @@ -175,6 +176,11 @@ static void vhost_user_blk_stop(VirtIODevice *vdev) VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); int ret; + if (!s->started_vu) { + return; + } + s->started_vu = false; + if (!k->set_guest_notifiers) { return; } @@ -341,9 +347,7 @@ static void vhost_user_blk_disconnect(DeviceState *dev) } s->connected = false; - if (s->dev.started) { - vhost_user_blk_stop(vdev); - } + vhost_user_blk_stop(vdev); vhost_dev_cleanup(&s->dev); } @@ -399,6 +403,15 @@ static void vhost_user_blk_event(void *opaque, QEMUChrEvent event) NULL, NULL, false); aio_bh_schedule_oneshot(ctx, vhost_user_blk_chr_closed_bh, opaque); } + + /* + * Move vhost device to the stopped state. The vhost-user device + * will be clean up and disconnected in BH. This can be useful in + * the vhost migration code. If disconnect was caught there is an + * option for the general vhost code to get the dev state without + * knowing its type (in this case vhost-user). + */ + s->dev.started = false; break; case CHR_EVENT_BREAK: case CHR_EVENT_MUX_IN: diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c index 39999c47c5..f4092b98cc 100644 --- a/hw/core/machine-hmp-cmds.c +++ b/hw/core/machine-hmp-cmds.c @@ -46,18 +46,6 @@ void hmp_info_cpus(Monitor *mon, const QDict *qdict) qapi_free_CpuInfoFastList(cpu_list); } -void hmp_cpu_add(Monitor *mon, const QDict *qdict) -{ - int cpuid; - Error *err = NULL; - - error_report("cpu_add is deprecated, please use device_add instead"); - - cpuid = qdict_get_int(qdict, "id"); - qmp_cpu_add(cpuid, &err); - hmp_handle_error(mon, err); -} - void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict) { Error *err = NULL; diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c index 21551221ad..5362c80a18 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -284,18 +284,6 @@ HotpluggableCPUList *qmp_query_hotpluggable_cpus(Error **errp) return machine_query_hotpluggable_cpus(ms); } -void qmp_cpu_add(int64_t id, Error **errp) -{ - MachineClass *mc; - - mc = MACHINE_GET_CLASS(current_machine); - if (mc->hot_add_cpu) { - mc->hot_add_cpu(current_machine, id, errp); - } else { - error_setg(errp, "Not supported"); - } -} - void qmp_set_numa_node(NumaOptions *cmd, Error **errp) { if (!runstate_check(RUN_STATE_PRECONFIG)) { diff --git a/hw/core/machine.c b/hw/core/machine.c index 9b02fb2f5e..d7f4a0d259 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -44,6 +44,7 @@ GlobalProperty hw_compat_5_0[] = { { "vmport", "x-signal-unsupported-cmd", "off" }, { "vmport", "x-report-vmx-type", "off" }, { "vmport", "x-cmds-v2", "off" }, + { "virtio-device", "x-disable-legacy-check", "true" }, }; const size_t hw_compat_5_0_len = G_N_ELEMENTS(hw_compat_5_0); diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 0e0535d2e3..8d14e4667a 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -95,6 +95,8 @@ typedef struct AcpiPmInfo { bool s3_disabled; bool s4_disabled; bool pcihp_bridge_en; + bool smi_on_cpuhp; + bool pcihp_root_en; uint8_t s4_val; AcpiFadtData fadt; uint16_t cpu_hp_io_base; @@ -194,6 +196,7 @@ static void acpi_get_pm_info(MachineState *machine, AcpiPmInfo *pm) pm->cpu_hp_io_base = 0; pm->pcihp_io_base = 0; pm->pcihp_io_len = 0; + pm->smi_on_cpuhp = false; assert(obj); init_common_fadt_data(machine, obj, &pm->fadt); @@ -207,12 +210,16 @@ static void acpi_get_pm_info(MachineState *machine, AcpiPmInfo *pm) object_property_get_uint(obj, ACPI_PCIHP_IO_LEN_PROP, NULL); } if (lpc) { + uint64_t smi_features = object_property_get_uint(lpc, + ICH9_LPC_SMI_NEGOTIATED_FEAT_PROP, NULL); struct AcpiGenericAddress r = { .space_id = AML_AS_SYSTEM_IO, .bit_width = 8, .address = ICH9_RST_CNT_IOPORT }; pm->fadt.reset_reg = r; pm->fadt.reset_val = 0xf; pm->fadt.flags |= 1 << ACPI_FADT_F_RESET_REG_SUP; pm->cpu_hp_io_base = ICH9_CPU_HOTPLUG_IO_BASE; + pm->smi_on_cpuhp = + !!(smi_features & BIT_ULL(ICH9_LPC_SMI_F_CPU_HOTPLUG_BIT)); } /* The above need not be conditional on machine type because the reset port @@ -245,6 +252,9 @@ static void acpi_get_pm_info(MachineState *machine, AcpiPmInfo *pm) pm->pcihp_bridge_en = object_property_get_bool(obj, "acpi-pci-hotplug-with-bridge-support", NULL); + pm->pcihp_root_en = + object_property_get_bool(obj, "acpi-root-pci-hotplug", + NULL); } static void acpi_get_misc_info(AcpiMiscInfo *info) @@ -359,6 +369,7 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus, int slot = PCI_SLOT(i); bool hotplug_enabled_dev; bool bridge_in_acpi; + bool cold_plugged_bridge; if (!pdev) { if (bsel) { /* add hotplug slots for non present devices */ @@ -380,15 +391,14 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus, pc = PCI_DEVICE_GET_CLASS(pdev); dc = DEVICE_GET_CLASS(pdev); - /* When hotplug for bridges is enabled, bridges are - * described in ACPI separately (see build_pci_bus_end). - * In this case they aren't themselves hot-pluggable. + /* + * Cold plugged bridges aren't themselves hot-pluggable. * Hotplugged bridges *are* hot-pluggable. */ - bridge_in_acpi = pc->is_bridge && pcihp_bridge_en && - !DEVICE(pdev)->hotplugged; + cold_plugged_bridge = pc->is_bridge && !DEVICE(pdev)->hotplugged; + bridge_in_acpi = cold_plugged_bridge && pcihp_bridge_en; - hotplug_enabled_dev = bsel && dc->hotpluggable && !bridge_in_acpi; + hotplug_enabled_dev = bsel && dc->hotpluggable && !cold_plugged_bridge; if (pc->class_id == PCI_CLASS_BRIDGE_ISA) { continue; @@ -450,10 +460,12 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus, } /* Append PCNT method to notify about events on local and child buses. - * Add unconditionally for root since DSDT expects it. + * Add this method for root bus only when hotplug is enabled since DSDT + * expects it. */ - method = aml_method("PCNT", 0, AML_NOTSERIALIZED); - + if (bsel || pcihp_bridge_en) { + method = aml_method("PCNT", 0, AML_NOTSERIALIZED); + } /* If bus supports hotplug select it and notify about local events */ if (bsel) { uint64_t bsel_val = qnum_get_uint(qobject_to(QNum, bsel)); @@ -479,7 +491,10 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus, aml_append(method, aml_name("^S%.02X.PCNT", devfn)); } } - aml_append(parent_scope, method); + + if (bsel || pcihp_bridge_en) { + aml_append(parent_scope, method); + } qobject_unref(bsel); } @@ -1504,7 +1519,9 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, build_hpet_aml(dsdt); build_piix4_isa_bridge(dsdt); build_isa_devices_aml(dsdt); - build_piix4_pci_hotplug(dsdt); + if (pm->pcihp_bridge_en || pm->pcihp_root_en) { + build_piix4_pci_hotplug(dsdt); + } build_piix4_pci0_int(dsdt); } else { sb_scope = aml_scope("_SB"); @@ -1515,6 +1532,32 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(dev, aml_name_decl("_UID", aml_int(0))); aml_append(dev, build_q35_osc_method()); aml_append(sb_scope, dev); + + if (pm->smi_on_cpuhp) { + /* reserve SMI block resources, IO ports 0xB2, 0xB3 */ + dev = aml_device("PCI0.SMI0"); + aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A06"))); + aml_append(dev, aml_name_decl("_UID", aml_string("SMI resources"))); + crs = aml_resource_template(); + aml_append(crs, + aml_io( + AML_DECODE16, + ACPI_PORT_SMI_CMD, + ACPI_PORT_SMI_CMD, + 1, + 2) + ); + aml_append(dev, aml_name_decl("_CRS", crs)); + aml_append(dev, aml_operation_region("SMIR", AML_SYSTEM_IO, + aml_int(ACPI_PORT_SMI_CMD), 2)); + field = aml_field("SMIR", AML_BYTE_ACC, AML_NOLOCK, + AML_WRITE_AS_ZEROS); + aml_append(field, aml_named_field("SMIC", 8)); + aml_append(field, aml_reserved_field(8)); + aml_append(dev, field); + aml_append(sb_scope, dev); + } + aml_append(dsdt, sb_scope); build_hpet_aml(dsdt); @@ -1530,7 +1573,8 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, build_legacy_cpu_hotplug_aml(dsdt, machine, pm->cpu_hp_io_base); } else { CPUHotplugFeatures opts = { - .acpi_1_compatible = true, .has_legacy_cphp = true + .acpi_1_compatible = true, .has_legacy_cphp = true, + .smi_path = pm->smi_on_cpuhp ? "\\_SB.PCI0.SMI0.SMIC" : NULL, }; build_cpus_aml(dsdt, machine, opts, pm->cpu_hp_io_base, "\\_SB.PCI0", "\\_GPE._E02"); @@ -1546,7 +1590,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, { aml_append(scope, aml_name_decl("_HID", aml_string("ACPI0006"))); - if (misc->is_piix4) { + if (misc->is_piix4 && (pm->pcihp_bridge_en || pm->pcihp_root_en)) { method = aml_method("_E01", 0, AML_NOTSERIALIZED); aml_append(method, aml_acquire(aml_name("\\_SB.PCI0.BLCK"), 0xFFFF)); @@ -1698,7 +1742,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, crs_range_set_free(&crs_range_set); /* reserve PCIHP resources */ - if (pm->pcihp_io_len) { + if (pm->pcihp_io_len && (pm->pcihp_bridge_en || pm->pcihp_root_en)) { dev = aml_device("PHPR"); aml_append(dev, aml_name_decl("_HID", aml_string("PNP0A06"))); aml_append(dev, diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 1e2ab5ebe7..2c6194e57f 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -97,7 +97,9 @@ #include "trace.h" #include CONFIG_DEVICES -GlobalProperty pc_compat_5_1[] = {}; +GlobalProperty pc_compat_5_1[] = { + { "ICH9-LPC", "x-smi-cpu-hotplug", "off" }, +}; const size_t pc_compat_5_1_len = G_N_ELEMENTS(pc_compat_5_1); GlobalProperty pc_compat_5_0[] = { @@ -769,32 +771,6 @@ void pc_smp_parse(MachineState *ms, QemuOpts *opts) } } -void pc_hot_add_cpu(MachineState *ms, const int64_t id, Error **errp) -{ - X86MachineState *x86ms = X86_MACHINE(ms); - int64_t apic_id = x86_cpu_apic_id_from_index(x86ms, id); - Error *local_err = NULL; - - if (id < 0) { - error_setg(errp, "Invalid CPU id: %" PRIi64, id); - return; - } - - if (apic_id >= ACPI_CPU_HOTPLUG_ID_LIMIT) { - error_setg(errp, "Unable to add CPU: %" PRIi64 - ", resulting APIC ID (%" PRIi64 ") is too large", - id, apic_id); - return; - } - - - x86_cpu_new(X86_MACHINE(ms), apic_id, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } -} - static void pc_machine_done(Notifier *notifier, void *data) { @@ -1691,7 +1667,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) mc->auto_enable_numa_with_memdev = true; mc->has_hotpluggable_cpus = true; mc->default_boot_order = "cad"; - mc->hot_add_cpu = pc_hot_add_cpu; mc->smp_parse = pc_smp_parse; mc->block_default_type = IF_IDE; mc->max_cpus = 255; diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 6f3e78bb60..2d8413a0ce 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -752,7 +752,6 @@ static void pc_i440fx_1_4_machine_options(MachineClass *m) { pc_i440fx_1_5_machine_options(m); m->hw_version = "1.4.0"; - m->hot_add_cpu = NULL; compat_props_add(m->compat_props, pc_compat_1_4, pc_compat_1_4_len); } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 622d039717..e1c415f57d 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -382,7 +382,7 @@ static void pc_q35_5_0_machine_options(MachineClass *m) m->numa_mem_supported = true; compat_props_add(m->compat_props, hw_compat_5_0, hw_compat_5_0_len); compat_props_add(m->compat_props, pc_compat_5_0, pc_compat_5_0_len); - m->auto_enable_numa_with_memhp = false; + m->auto_enable_numa_with_memdev = false; } DEFINE_Q35_MACHINE(v5_0, "pc-q35-5.0", NULL, diff --git a/hw/i386/x86.c b/hw/i386/x86.c index c2ea989579..403c2b1dad 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -279,6 +279,17 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, return; } + if (x86ms->acpi_dev) { + Error *local_err = NULL; + + hotplug_handler_pre_plug(HOTPLUG_HANDLER(x86ms->acpi_dev), dev, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + } + init_topo_info(&topo_info, x86ms); env->nr_dies = x86ms->smp_dies; diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index 3303d2eab6..04e5323140 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -373,6 +373,15 @@ static void smi_features_ok_callback(void *opaque) /* guest requests invalid features, leave @features_ok at zero */ return; } + if (!(guest_features & BIT_ULL(ICH9_LPC_SMI_F_BROADCAST_BIT)) && + guest_features & (BIT_ULL(ICH9_LPC_SMI_F_CPU_HOTPLUG_BIT) | + BIT_ULL(ICH9_LPC_SMI_F_CPU_HOT_UNPLUG_BIT))) { + /* + * cpu hot-[un]plug with SMI requires SMI broadcast, + * leave @features_ok at zero + */ + return; + } /* valid feature subset requested, lock it down, report success */ lpc->smi_negotiated_features = guest_features; @@ -638,6 +647,9 @@ static void ich9_lpc_initfn(Object *obj) &acpi_enable_cmd, OBJ_PROP_FLAG_READ); object_property_add_uint8_ptr(OBJECT(lpc), ACPI_PM_PROP_ACPI_DISABLE_CMD, &acpi_disable_cmd, OBJ_PROP_FLAG_READ); + object_property_add_uint64_ptr(obj, ICH9_LPC_SMI_NEGOTIATED_FEAT_PROP, + &lpc->smi_negotiated_features, + OBJ_PROP_FLAG_READ); ich9_pm_add_properties(obj, &lpc->pm); } @@ -747,6 +759,10 @@ static Property ich9_lpc_properties[] = { DEFINE_PROP_BOOL("noreboot", ICH9LPCState, pin_strap.spkr_hi, true), DEFINE_PROP_BIT64("x-smi-broadcast", ICH9LPCState, smi_host_features, ICH9_LPC_SMI_F_BROADCAST_BIT, true), + DEFINE_PROP_BIT64("x-smi-cpu-hotplug", ICH9LPCState, smi_host_features, + ICH9_LPC_SMI_F_CPU_HOTPLUG_BIT, true), + DEFINE_PROP_BIT64("x-smi-cpu-hotunplug", ICH9LPCState, smi_host_features, + ICH9_LPC_SMI_F_CPU_HOT_UNPLUG_BIT, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 3106bbea33..28266a3a35 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -553,17 +553,6 @@ static HotplugHandler *s390_get_hotplug_handler(MachineState *machine, return NULL; } -static void s390_hot_add_cpu(MachineState *machine, - const int64_t id, Error **errp) -{ - ObjectClass *oc; - - g_assert(machine->possible_cpus->cpus[0].cpu); - oc = OBJECT_CLASS(CPU_GET_CLASS(machine->possible_cpus->cpus[0].cpu)); - - s390x_new_cpu(object_class_get_name(oc), id, errp); -} - static void s390_nmi(NMIState *n, int cpu_index, Error **errp) { CPUState *cs = qemu_get_cpu(cpu_index); @@ -604,7 +593,6 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) s390mc->hpage_1m_allowed = true; mc->init = ccw_init; mc->reset = s390_machine_reset; - mc->hot_add_cpu = s390_hot_add_cpu; mc->block_default_type = IF_VIRTIO; mc->no_cdrom = 1; mc->no_floppy = 1; diff --git a/hw/s390x/vhost-vsock-ccw.c b/hw/s390x/vhost-vsock-ccw.c index 0822ecca89..246416a8f9 100644 --- a/hw/s390x/vhost-vsock-ccw.c +++ b/hw/s390x/vhost-vsock-ccw.c @@ -40,9 +40,21 @@ static void vhost_vsock_ccw_class_init(ObjectClass *klass, void *data) static void vhost_vsock_ccw_instance_init(Object *obj) { VHostVSockCCWState *dev = VHOST_VSOCK_CCW(obj); + VirtioCcwDevice *ccw_dev = VIRTIO_CCW_DEVICE(obj); + VirtIODevice *virtio_dev; virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VHOST_VSOCK); + + virtio_dev = VIRTIO_DEVICE(&dev->vdev); + + /* + * To avoid migration issues, we force virtio version 1 only when + * legacy check is enabled in the new machine types (>= 5.1). + */ + if (!virtio_legacy_check_disabled(virtio_dev)) { + ccw_dev->force_revision_1 = true; + } } static const TypeInfo vhost_vsock_ccw_info = { diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 8d140dc50f..4582e94ae7 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -1122,9 +1122,18 @@ static void virtio_ccw_device_plugged(DeviceState *d, Error **errp) } if (!virtio_ccw_rev_max(dev) && !virtio_legacy_allowed(vdev)) { - error_setg(errp, "Invalid value of property max_rev " - "(is %d expected >= 1)", virtio_ccw_rev_max(dev)); - return; + /* + * To avoid migration issues, we allow legacy mode when legacy + * check is disabled in the old machine types (< 5.1). + */ + if (virtio_legacy_check_disabled(vdev)) { + warn_report("device requires revision >= 1, but for backward " + "compatibility max_revision=0 is allowed"); + } else { + error_setg(errp, "Invalid value of property max_rev " + "(is %d expected >= 1)", virtio_ccw_rev_max(dev)); + return; + } } if (virtio_get_num_queues(vdev) > VIRTIO_QUEUE_MAX) { diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index 7cc950b41c..8b30906e50 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -110,7 +110,7 @@ static struct { static struct { size_t nvalues; - const char **values; + char **values; } type11; static struct { @@ -314,6 +314,11 @@ static const QemuOptDesc qemu_smbios_type11_opts[] = { .type = QEMU_OPT_STRING, .help = "OEM string data", }, + { + .name = "path", + .type = QEMU_OPT_STRING, + .help = "OEM string data from file", + }, }; static const QemuOptDesc qemu_smbios_type17_opts[] = { @@ -360,6 +365,13 @@ static void smbios_register_config(void) opts_init(smbios_register_config); +/* + * The SMBIOS 2.1 "structure table length" field in the + * entry point uses a 16-bit integer, so we're limited + * in total table size + */ +#define SMBIOS_21_MAX_TABLES_LEN 0xffff + static void smbios_validate_table(MachineState *ms) { uint32_t expect_t4_count = smbios_legacy ? @@ -370,6 +382,13 @@ static void smbios_validate_table(MachineState *ms) expect_t4_count, smbios_type4_count); exit(1); } + + if (smbios_ep_type == SMBIOS_ENTRY_POINT_21 && + smbios_tables_len > SMBIOS_21_MAX_TABLES_LEN) { + error_report("SMBIOS 2.1 table length %zu exceeds %d", + smbios_tables_len, SMBIOS_21_MAX_TABLES_LEN); + exit(1); + } } @@ -641,6 +660,8 @@ static void smbios_build_type_11_table(void) for (i = 0; i < type11.nvalues; i++) { SMBIOS_TABLE_SET_STR_LIST(11, type11.values[i]); + g_free(type11.values[i]); + type11.values[i] = NULL; } SMBIOS_BUILD_TABLE_POST; @@ -940,9 +961,8 @@ static void save_opt(const char **dest, QemuOpts *opts, const char *name) struct opt_list { - const char *name; size_t *ndest; - const char ***dest; + char ***dest; }; static int save_opt_one(void *opaque, @@ -951,23 +971,60 @@ static int save_opt_one(void *opaque, { struct opt_list *opt = opaque; - if (!g_str_equal(name, opt->name)) { - return 0; + if (g_str_equal(name, "path")) { + g_autoptr(GByteArray) data = g_byte_array_new(); + g_autofree char *buf = g_new(char, 4096); + ssize_t ret; + int fd = qemu_open(value, O_RDONLY, errp); + if (fd < 0) { + return -1; + } + + while (1) { + ret = read(fd, buf, 4096); + if (ret == 0) { + break; + } + if (ret < 0) { + error_setg(errp, "Unable to read from %s: %s", + value, strerror(errno)); + return -1; + } + if (memchr(buf, '\0', ret)) { + error_setg(errp, "NUL in OEM strings value in %s", value); + return -1; + } + g_byte_array_append(data, (guint8 *)buf, ret); + } + + close(fd); + + *opt->dest = g_renew(char *, *opt->dest, (*opt->ndest) + 1); + (*opt->dest)[*opt->ndest] = (char *)g_byte_array_free(data, FALSE); + (*opt->ndest)++; + data = NULL; + } else if (g_str_equal(name, "value")) { + *opt->dest = g_renew(char *, *opt->dest, (*opt->ndest) + 1); + (*opt->dest)[*opt->ndest] = g_strdup(value); + (*opt->ndest)++; + } else if (!g_str_equal(name, "type")) { + error_setg(errp, "Unexpected option %s", name); + return -1; } - *opt->dest = g_renew(const char *, *opt->dest, (*opt->ndest) + 1); - (*opt->dest)[*opt->ndest] = value; - (*opt->ndest)++; return 0; } -static void save_opt_list(size_t *ndest, const char ***dest, - QemuOpts *opts, const char *name) +static bool save_opt_list(size_t *ndest, char ***dest, QemuOpts *opts, + Error **errp) { struct opt_list opt = { - name, ndest, dest, + ndest, dest, }; - qemu_opt_foreach(opts, save_opt_one, &opt, NULL); + if (!qemu_opt_foreach(opts, save_opt_one, &opt, errp)) { + return false; + } + return true; } void smbios_entry_add(QemuOpts *opts, Error **errp) @@ -1149,7 +1206,9 @@ void smbios_entry_add(QemuOpts *opts, Error **errp) if (!qemu_opts_validate(opts, qemu_smbios_type11_opts, errp)) { return; } - save_opt_list(&type11.nvalues, &type11.values, opts, "value"); + if (!save_opt_list(&type11.nvalues, &type11.values, opts, errp)) { + return; + } return; case 17: if (!qemu_opts_validate(opts, qemu_smbios_type17_opts, errp)) { diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index 845200bf10..cf1e59de30 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -22,6 +22,37 @@ vhost_user_postcopy_waker(const char *rb, uint64_t rb_offset) "%s + 0x%"PRIx64 vhost_user_postcopy_waker_found(uint64_t client_addr) "0x%"PRIx64 vhost_user_postcopy_waker_nomatch(const char *rb, uint64_t rb_offset) "%s + 0x%"PRIx64 +# vhost-vdpa.c +vhost_vdpa_dma_map(void *vdpa, int fd, uint32_t msg_type, uint64_t iova, uint64_t size, uint64_t uaddr, uint8_t perm, uint8_t type) "vdpa:%p fd: %d msg_type: %"PRIu32" iova: 0x%"PRIx64" size: 0x%"PRIx64" uaddr: 0x%"PRIx64" perm: 0x%"PRIx8" type: %"PRIu8 +vhost_vdpa_dma_unmap(void *vdpa, int fd, uint32_t msg_type, uint64_t iova, uint64_t size, uint8_t type) "vdpa:%p fd: %d msg_type: %"PRIu32" iova: 0x%"PRIx64" size: 0x%"PRIx64" type: %"PRIu8 +vhost_vdpa_listener_region_add(void *vdpa, uint64_t iova, uint64_t llend, void *vaddr, bool readonly) "vdpa: %p iova 0x%"PRIx64" llend 0x%"PRIx64" vaddr: %p read-only: %d" +vhost_vdpa_listener_region_del(void *vdpa, uint64_t iova, uint64_t llend) "vdpa: %p iova 0x%"PRIx64" llend 0x%"PRIx64 +vhost_vdpa_add_status(void *dev, uint8_t status) "dev: %p status: 0x%"PRIx8 +vhost_vdpa_init(void *dev, void *vdpa) "dev: %p vdpa: %p" +vhost_vdpa_cleanup(void *dev, void *vdpa) "dev: %p vdpa: %p" +vhost_vdpa_memslots_limit(void *dev, int ret) "dev: %p = 0x%x" +vhost_vdpa_set_mem_table(void *dev, uint32_t nregions, uint32_t padding) "dev: %p nregions: %"PRIu32" padding: 0x%"PRIx32 +vhost_vdpa_dump_regions(void *dev, int i, uint64_t guest_phys_addr, uint64_t memory_size, uint64_t userspace_addr, uint64_t flags_padding) "dev: %p %d: guest_phys_addr: 0x%"PRIx64" memory_size: 0x%"PRIx64" userspace_addr: 0x%"PRIx64" flags_padding: 0x%"PRIx64 +vhost_vdpa_set_features(void *dev, uint64_t features) "dev: %p features: 0x%"PRIx64 +vhost_vdpa_get_device_id(void *dev, uint32_t device_id) "dev: %p device_id %"PRIu32 +vhost_vdpa_reset_device(void *dev, uint8_t status) "dev: %p status: 0x%"PRIx8 +vhost_vdpa_get_vq_index(void *dev, int idx, int vq_idx) "dev: %p idx: %d vq idx: %d" +vhost_vdpa_set_vring_ready(void *dev) "dev: %p" +vhost_vdpa_dump_config(void *dev, const char *line) "dev: %p %s" +vhost_vdpa_set_config(void *dev, uint32_t offset, uint32_t size, uint32_t flags) "dev: %p offset: %"PRIu32" size: %"PRIu32" flags: 0x%"PRIx32 +vhost_vdpa_get_config(void *dev, void *config, uint32_t config_len) "dev: %p config: %p config_len: %"PRIu32 +vhost_vdpa_dev_start(void *dev, bool started) "dev: %p started: %d" +vhost_vdpa_set_log_base(void *dev, uint64_t base, unsigned long long size, int refcnt, int fd, void *log) "dev: %p base: 0x%"PRIx64" size: %llu refcnt: %d fd: %d log: %p" +vhost_vdpa_set_vring_addr(void *dev, unsigned int index, unsigned int flags, uint64_t desc_user_addr, uint64_t used_user_addr, uint64_t avail_user_addr, uint64_t log_guest_addr) "dev: %p index: %u flags: 0x%x desc_user_addr: 0x%"PRIx64" used_user_addr: 0x%"PRIx64" avail_user_addr: 0x%"PRIx64" log_guest_addr: 0x%"PRIx64 +vhost_vdpa_set_vring_num(void *dev, unsigned int index, unsigned int num) "dev: %p index: %u num: %u" +vhost_vdpa_set_vring_base(void *dev, unsigned int index, unsigned int num) "dev: %p index: %u num: %u" +vhost_vdpa_get_vring_base(void *dev, unsigned int index, unsigned int num) "dev: %p index: %u num: %u" +vhost_vdpa_set_vring_kick(void *dev, unsigned int index, int fd) "dev: %p index: %u fd: %d" +vhost_vdpa_set_vring_call(void *dev, unsigned int index, int fd) "dev: %p index: %u fd: %d" +vhost_vdpa_get_features(void *dev, uint64_t features) "dev: %p features: 0x%"PRIx64 +vhost_vdpa_set_owner(void *dev) "dev: %p" +vhost_vdpa_vq_get_addr(void *dev, void *vq, uint64_t desc_user_addr, uint64_t avail_user_addr, uint64_t used_user_addr) "dev: %p vq: %p desc_user_addr: 0x%"PRIx64" avail_user_addr: 0x%"PRIx64" used_user_addr: 0x%"PRIx64 + # virtio.c virtqueue_alloc_element(void *elem, size_t sz, unsigned in_num, unsigned out_num) "elem %p size %zd in_num %u out_num %u" virtqueue_fill(void *vq, const void *elem, unsigned int len, unsigned int idx) "vq %p elem %p len %u idx %u" diff --git a/hw/virtio/vhost-backend.c b/hw/virtio/vhost-backend.c index 782b1d67d9..88c8ecc9e0 100644 --- a/hw/virtio/vhost-backend.c +++ b/hw/virtio/vhost-backend.c @@ -156,6 +156,28 @@ static int vhost_kernel_set_features(struct vhost_dev *dev, return vhost_kernel_call(dev, VHOST_SET_FEATURES, &features); } +static int vhost_kernel_set_backend_cap(struct vhost_dev *dev) +{ + uint64_t features; + uint64_t f = 0x1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2; + int r; + + if (vhost_kernel_call(dev, VHOST_GET_BACKEND_FEATURES, &features)) { + return 0; + } + + features &= f; + r = vhost_kernel_call(dev, VHOST_SET_BACKEND_FEATURES, + &features); + if (r) { + return 0; + } + + dev->backend_cap = features; + + return 0; +} + static int vhost_kernel_get_features(struct vhost_dev *dev, uint64_t *features) { @@ -195,34 +217,65 @@ static int vhost_kernel_vsock_set_running(struct vhost_dev *dev, int start) static void vhost_kernel_iotlb_read(void *opaque) { struct vhost_dev *dev = opaque; - struct vhost_msg msg; ssize_t len; - while ((len = read((uintptr_t)dev->opaque, &msg, sizeof msg)) > 0) { - if (len < sizeof msg) { - error_report("Wrong vhost message len: %d", (int)len); - break; + if (dev->backend_cap & + (0x1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2)) { + struct vhost_msg_v2 msg; + + while ((len = read((uintptr_t)dev->opaque, &msg, sizeof msg)) > 0) { + if (len < sizeof msg) { + error_report("Wrong vhost message len: %d", (int)len); + break; + } + if (msg.type != VHOST_IOTLB_MSG_V2) { + error_report("Unknown vhost iotlb message type"); + break; + } + + vhost_backend_handle_iotlb_msg(dev, &msg.iotlb); } - if (msg.type != VHOST_IOTLB_MSG) { - error_report("Unknown vhost iotlb message type"); - break; + } else { + struct vhost_msg msg; + + while ((len = read((uintptr_t)dev->opaque, &msg, sizeof msg)) > 0) { + if (len < sizeof msg) { + error_report("Wrong vhost message len: %d", (int)len); + break; + } + if (msg.type != VHOST_IOTLB_MSG) { + error_report("Unknown vhost iotlb message type"); + break; + } + + vhost_backend_handle_iotlb_msg(dev, &msg.iotlb); } - - vhost_backend_handle_iotlb_msg(dev, &msg.iotlb); } } static int vhost_kernel_send_device_iotlb_msg(struct vhost_dev *dev, struct vhost_iotlb_msg *imsg) { - struct vhost_msg msg; + if (dev->backend_cap & (1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2)) { + struct vhost_msg_v2 msg; - msg.type = VHOST_IOTLB_MSG; - msg.iotlb = *imsg; + msg.type = VHOST_IOTLB_MSG_V2; + msg.iotlb = *imsg; - if (write((uintptr_t)dev->opaque, &msg, sizeof msg) != sizeof msg) { - error_report("Fail to update device iotlb"); - return -EFAULT; + if (write((uintptr_t)dev->opaque, &msg, sizeof msg) != sizeof msg) { + error_report("Fail to update device iotlb"); + return -EFAULT; + } + } else { + struct vhost_msg msg; + + msg.type = VHOST_IOTLB_MSG; + msg.iotlb = *imsg; + + if (write((uintptr_t)dev->opaque, &msg, sizeof msg) != sizeof msg) { + error_report("Fail to update device iotlb"); + return -EFAULT; + } } return 0; @@ -260,6 +313,7 @@ static const VhostOps kernel_ops = { vhost_kernel_set_vring_busyloop_timeout, .vhost_set_features = vhost_kernel_set_features, .vhost_get_features = vhost_kernel_get_features, + .vhost_set_backend_cap = vhost_kernel_set_backend_cap, .vhost_set_owner = vhost_kernel_set_owner, .vhost_reset_device = vhost_kernel_reset_device, .vhost_get_vq_index = vhost_kernel_get_vq_index, diff --git a/hw/virtio/vhost-user-vsock-pci.c b/hw/virtio/vhost-user-vsock-pci.c index 763f89984e..72a96199cd 100644 --- a/hw/virtio/vhost-user-vsock-pci.c +++ b/hw/virtio/vhost-user-vsock-pci.c @@ -41,6 +41,9 @@ static void vhost_user_vsock_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) VHostUserVSockPCI *dev = VHOST_USER_VSOCK_PCI(vpci_dev); DeviceState *vdev = DEVICE(&dev->vdev); + /* unlike vhost-vsock, we do not need to care about pre-5.1 compat */ + virtio_pci_force_virtio_1(vpci_dev); + qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } @@ -69,7 +72,6 @@ static void vhost_user_vsock_pci_instance_init(Object *obj) static const VirtioPCIDeviceTypeInfo vhost_user_vsock_pci_info = { .base_name = TYPE_VHOST_USER_VSOCK_PCI, .generic_name = "vhost-user-vsock-pci", - .transitional_name = "vhost-user-vsock-pci-transitional", .non_transitional_name = "vhost-user-vsock-pci-non-transitional", .instance_size = sizeof(VHostUserVSockPCI), .instance_init = vhost_user_vsock_pci_instance_init, diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index aedc52c4d7..4f1039910a 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -20,6 +20,8 @@ #include "hw/virtio/vhost-vdpa.h" #include "qemu/main-loop.h" #include "cpu.h" +#include "trace.h" +#include "qemu-common.h" static bool vhost_vdpa_listener_skipped_section(MemoryRegionSection *section) { @@ -48,6 +50,9 @@ static int vhost_vdpa_dma_map(struct vhost_vdpa *v, hwaddr iova, hwaddr size, msg.iotlb.perm = readonly ? VHOST_ACCESS_RO : VHOST_ACCESS_RW; msg.iotlb.type = VHOST_IOTLB_UPDATE; + trace_vhost_vdpa_dma_map(v, fd, msg.type, msg.iotlb.iova, msg.iotlb.size, + msg.iotlb.uaddr, msg.iotlb.perm, msg.iotlb.type); + if (write(fd, &msg, sizeof(msg)) != sizeof(msg)) { error_report("failed to write, fd=%d, errno=%d (%s)", fd, errno, strerror(errno)); @@ -69,6 +74,9 @@ static int vhost_vdpa_dma_unmap(struct vhost_vdpa *v, hwaddr iova, msg.iotlb.size = size; msg.iotlb.type = VHOST_IOTLB_INVALIDATE; + trace_vhost_vdpa_dma_unmap(v, fd, msg.type, msg.iotlb.iova, + msg.iotlb.size, msg.iotlb.type); + if (write(fd, &msg, sizeof(msg)) != sizeof(msg)) { error_report("failed to write, fd=%d, errno=%d (%s)", fd, errno, strerror(errno)); @@ -78,6 +86,46 @@ static int vhost_vdpa_dma_unmap(struct vhost_vdpa *v, hwaddr iova, return ret; } +static void vhost_vdpa_listener_begin(MemoryListener *listener) +{ + struct vhost_vdpa *v = container_of(listener, struct vhost_vdpa, listener); + struct vhost_dev *dev = v->dev; + struct vhost_msg_v2 msg; + int fd = v->device_fd; + + if (!(dev->backend_cap & (0x1ULL << VHOST_BACKEND_F_IOTLB_BATCH))) { + return; + } + + msg.type = v->msg_type; + msg.iotlb.type = VHOST_IOTLB_BATCH_BEGIN; + + if (write(fd, &msg, sizeof(msg)) != sizeof(msg)) { + error_report("failed to write, fd=%d, errno=%d (%s)", + fd, errno, strerror(errno)); + } +} + +static void vhost_vdpa_listener_commit(MemoryListener *listener) +{ + struct vhost_vdpa *v = container_of(listener, struct vhost_vdpa, listener); + struct vhost_dev *dev = v->dev; + struct vhost_msg_v2 msg; + int fd = v->device_fd; + + if (!(dev->backend_cap & (0x1ULL << VHOST_BACKEND_F_IOTLB_BATCH))) { + return; + } + + msg.type = v->msg_type; + msg.iotlb.type = VHOST_IOTLB_BATCH_END; + + if (write(fd, &msg, sizeof(msg)) != sizeof(msg)) { + error_report("failed to write, fd=%d, errno=%d (%s)", + fd, errno, strerror(errno)); + } +} + static void vhost_vdpa_listener_region_add(MemoryListener *listener, MemoryRegionSection *section) { @@ -114,6 +162,9 @@ static void vhost_vdpa_listener_region_add(MemoryListener *listener, section->offset_within_region + (iova - section->offset_within_address_space); + trace_vhost_vdpa_listener_region_add(v, iova, int128_get64(llend), + vaddr, section->readonly); + llsize = int128_sub(llend, int128_make64(iova)); ret = vhost_vdpa_dma_map(v, iova, int128_get64(llsize), @@ -169,6 +220,8 @@ static void vhost_vdpa_listener_region_del(MemoryListener *listener, llend = int128_add(llend, section->size); llend = int128_and(llend, int128_exts64(TARGET_PAGE_MASK)); + trace_vhost_vdpa_listener_region_del(v, iova, int128_get64(llend)); + if (int128_ge(int128_make64(iova), llend)) { return; } @@ -188,6 +241,8 @@ static void vhost_vdpa_listener_region_del(MemoryListener *listener, * depends on the addnop(). */ static const MemoryListener vhost_vdpa_memory_listener = { + .begin = vhost_vdpa_listener_begin, + .commit = vhost_vdpa_listener_commit, .region_add = vhost_vdpa_listener_region_add, .region_del = vhost_vdpa_listener_region_del, }; @@ -207,6 +262,7 @@ static void vhost_vdpa_add_status(struct vhost_dev *dev, uint8_t status) { uint8_t s; + trace_vhost_vdpa_add_status(dev, status); if (vhost_vdpa_call(dev, VHOST_VDPA_GET_STATUS, &s)) { return; } @@ -221,8 +277,10 @@ static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque) struct vhost_vdpa *v; uint64_t features; assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_VDPA); + trace_vhost_vdpa_init(dev, opaque); v = opaque; + v->dev = dev; dev->opaque = opaque ; vhost_vdpa_call(dev, VHOST_GET_FEATURES, &features); dev->backend_features = features; @@ -240,6 +298,7 @@ static int vhost_vdpa_cleanup(struct vhost_dev *dev) struct vhost_vdpa *v; assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_VDPA); v = dev->opaque; + trace_vhost_vdpa_cleanup(dev, v); memory_listener_unregister(&v->listener); dev->opaque = NULL; @@ -248,13 +307,25 @@ static int vhost_vdpa_cleanup(struct vhost_dev *dev) static int vhost_vdpa_memslots_limit(struct vhost_dev *dev) { + trace_vhost_vdpa_memslots_limit(dev, INT_MAX); return INT_MAX; } static int vhost_vdpa_set_mem_table(struct vhost_dev *dev, struct vhost_memory *mem) { - + trace_vhost_vdpa_set_mem_table(dev, mem->nregions, mem->padding); + if (trace_event_get_state_backends(TRACE_VHOST_VDPA_SET_MEM_TABLE) && + trace_event_get_state_backends(TRACE_VHOST_VDPA_DUMP_REGIONS)) { + int i; + for (i = 0; i < mem->nregions; i++) { + trace_vhost_vdpa_dump_regions(dev, i, + mem->regions[i].guest_phys_addr, + mem->regions[i].memory_size, + mem->regions[i].userspace_addr, + mem->regions[i].flags_padding); + } + } if (mem->padding) { return -1; } @@ -266,6 +337,7 @@ static int vhost_vdpa_set_features(struct vhost_dev *dev, uint64_t features) { int ret; + trace_vhost_vdpa_set_features(dev, features); ret = vhost_vdpa_call(dev, VHOST_SET_FEATURES, &features); uint8_t status = 0; if (ret) { @@ -277,29 +349,59 @@ static int vhost_vdpa_set_features(struct vhost_dev *dev, return !(status & VIRTIO_CONFIG_S_FEATURES_OK); } +static int vhost_vdpa_set_backend_cap(struct vhost_dev *dev) +{ + uint64_t features; + uint64_t f = 0x1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2 | + 0x1ULL << VHOST_BACKEND_F_IOTLB_BATCH; + int r; + + if (vhost_vdpa_call(dev, VHOST_GET_BACKEND_FEATURES, &features)) { + return 0; + } + + features &= f; + r = vhost_vdpa_call(dev, VHOST_SET_BACKEND_FEATURES, &features); + if (r) { + return 0; + } + + dev->backend_cap = features; + + return 0; +} + int vhost_vdpa_get_device_id(struct vhost_dev *dev, uint32_t *device_id) { - return vhost_vdpa_call(dev, VHOST_VDPA_GET_DEVICE_ID, device_id); + int ret; + ret = vhost_vdpa_call(dev, VHOST_VDPA_GET_DEVICE_ID, device_id); + trace_vhost_vdpa_get_device_id(dev, *device_id); + return ret; } static int vhost_vdpa_reset_device(struct vhost_dev *dev) { + int ret; uint8_t status = 0; - return vhost_vdpa_call(dev, VHOST_VDPA_SET_STATUS, &status); + ret = vhost_vdpa_call(dev, VHOST_VDPA_SET_STATUS, &status); + trace_vhost_vdpa_reset_device(dev, status); + return ret; } static int vhost_vdpa_get_vq_index(struct vhost_dev *dev, int idx) { assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs); + trace_vhost_vdpa_get_vq_index(dev, idx, idx - dev->vq_index); return idx - dev->vq_index; } static int vhost_vdpa_set_vring_ready(struct vhost_dev *dev) { int i; + trace_vhost_vdpa_set_vring_ready(dev); for (i = 0; i < dev->nvqs; ++i) { struct vhost_vring_state state = { .index = dev->vq_index + i, @@ -310,6 +412,19 @@ static int vhost_vdpa_set_vring_ready(struct vhost_dev *dev) return 0; } +static void vhost_vdpa_dump_config(struct vhost_dev *dev, const uint8_t *config, + uint32_t config_len) +{ + int b, len; + char line[QEMU_HEXDUMP_LINE_LEN]; + + for (b = 0; b < config_len; b += 16) { + len = config_len - b; + qemu_hexdump_line(line, b, config, len, false); + trace_vhost_vdpa_dump_config(dev, line); + } +} + static int vhost_vdpa_set_config(struct vhost_dev *dev, const uint8_t *data, uint32_t offset, uint32_t size, uint32_t flags) @@ -318,10 +433,15 @@ static int vhost_vdpa_set_config(struct vhost_dev *dev, const uint8_t *data, int ret; unsigned long config_size = offsetof(struct vhost_vdpa_config, buf); + trace_vhost_vdpa_set_config(dev, offset, size, flags); config = g_malloc(size + config_size); config->off = offset; config->len = size; memcpy(config->buf, data, size); + if (trace_event_get_state_backends(TRACE_VHOST_VDPA_SET_CONFIG) && + trace_event_get_state_backends(TRACE_VHOST_VDPA_DUMP_CONFIG)) { + vhost_vdpa_dump_config(dev, data, size); + } ret = vhost_vdpa_call(dev, VHOST_VDPA_SET_CONFIG, config); g_free(config); return ret; @@ -334,18 +454,24 @@ static int vhost_vdpa_get_config(struct vhost_dev *dev, uint8_t *config, unsigned long config_size = offsetof(struct vhost_vdpa_config, buf); int ret; + trace_vhost_vdpa_get_config(dev, config, config_len); v_config = g_malloc(config_len + config_size); v_config->len = config_len; v_config->off = 0; ret = vhost_vdpa_call(dev, VHOST_VDPA_GET_CONFIG, v_config); memcpy(config, v_config->buf, config_len); g_free(v_config); + if (trace_event_get_state_backends(TRACE_VHOST_VDPA_GET_CONFIG) && + trace_event_get_state_backends(TRACE_VHOST_VDPA_DUMP_CONFIG)) { + vhost_vdpa_dump_config(dev, config, config_len); + } return ret; } static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started) { struct vhost_vdpa *v = dev->opaque; + trace_vhost_vdpa_dev_start(dev, started); if (started) { uint8_t status = 0; memory_listener_register(&v->listener, &address_space_memory); @@ -367,53 +493,72 @@ static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started) static int vhost_vdpa_set_log_base(struct vhost_dev *dev, uint64_t base, struct vhost_log *log) { + trace_vhost_vdpa_set_log_base(dev, base, log->size, log->refcnt, log->fd, + log->log); return vhost_vdpa_call(dev, VHOST_SET_LOG_BASE, &base); } static int vhost_vdpa_set_vring_addr(struct vhost_dev *dev, struct vhost_vring_addr *addr) { + trace_vhost_vdpa_set_vring_addr(dev, addr->index, addr->flags, + addr->desc_user_addr, addr->used_user_addr, + addr->avail_user_addr, + addr->log_guest_addr); return vhost_vdpa_call(dev, VHOST_SET_VRING_ADDR, addr); } static int vhost_vdpa_set_vring_num(struct vhost_dev *dev, struct vhost_vring_state *ring) { + trace_vhost_vdpa_set_vring_num(dev, ring->index, ring->num); return vhost_vdpa_call(dev, VHOST_SET_VRING_NUM, ring); } static int vhost_vdpa_set_vring_base(struct vhost_dev *dev, struct vhost_vring_state *ring) { + trace_vhost_vdpa_set_vring_base(dev, ring->index, ring->num); return vhost_vdpa_call(dev, VHOST_SET_VRING_BASE, ring); } static int vhost_vdpa_get_vring_base(struct vhost_dev *dev, struct vhost_vring_state *ring) { - return vhost_vdpa_call(dev, VHOST_GET_VRING_BASE, ring); + int ret; + + ret = vhost_vdpa_call(dev, VHOST_GET_VRING_BASE, ring); + trace_vhost_vdpa_get_vring_base(dev, ring->index, ring->num); + return ret; } static int vhost_vdpa_set_vring_kick(struct vhost_dev *dev, struct vhost_vring_file *file) { + trace_vhost_vdpa_set_vring_kick(dev, file->index, file->fd); return vhost_vdpa_call(dev, VHOST_SET_VRING_KICK, file); } static int vhost_vdpa_set_vring_call(struct vhost_dev *dev, struct vhost_vring_file *file) { + trace_vhost_vdpa_set_vring_call(dev, file->index, file->fd); return vhost_vdpa_call(dev, VHOST_SET_VRING_CALL, file); } static int vhost_vdpa_get_features(struct vhost_dev *dev, uint64_t *features) { - return vhost_vdpa_call(dev, VHOST_GET_FEATURES, features); + int ret; + + ret = vhost_vdpa_call(dev, VHOST_GET_FEATURES, features); + trace_vhost_vdpa_get_features(dev, *features); + return ret; } static int vhost_vdpa_set_owner(struct vhost_dev *dev) { + trace_vhost_vdpa_set_owner(dev); return vhost_vdpa_call(dev, VHOST_SET_OWNER, NULL); } @@ -424,6 +569,8 @@ static int vhost_vdpa_vq_get_addr(struct vhost_dev *dev, addr->desc_user_addr = (uint64_t)(unsigned long)vq->desc_phys; addr->avail_user_addr = (uint64_t)(unsigned long)vq->avail_phys; addr->used_user_addr = (uint64_t)(unsigned long)vq->used_phys; + trace_vhost_vdpa_vq_get_addr(dev, vq, addr->desc_user_addr, + addr->avail_user_addr, addr->used_user_addr); return 0; } @@ -444,6 +591,7 @@ const VhostOps vdpa_ops = { .vhost_set_vring_kick = vhost_vdpa_set_vring_kick, .vhost_set_vring_call = vhost_vdpa_set_vring_call, .vhost_get_features = vhost_vdpa_get_features, + .vhost_set_backend_cap = vhost_vdpa_set_backend_cap, .vhost_set_owner = vhost_vdpa_set_owner, .vhost_set_vring_endian = NULL, .vhost_backend_memslots_limit = vhost_vdpa_memslots_limit, diff --git a/hw/virtio/vhost-vsock-pci.c b/hw/virtio/vhost-vsock-pci.c index e56067b427..205da8d1f5 100644 --- a/hw/virtio/vhost-vsock-pci.c +++ b/hw/virtio/vhost-vsock-pci.c @@ -44,6 +44,15 @@ static void vhost_vsock_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) { VHostVSockPCI *dev = VHOST_VSOCK_PCI(vpci_dev); DeviceState *vdev = DEVICE(&dev->vdev); + VirtIODevice *virtio_dev = VIRTIO_DEVICE(vdev); + + /* + * To avoid migration issues, we force virtio version 1 only when + * legacy check is enabled in the new machine types (>= 5.1). + */ + if (!virtio_legacy_check_disabled(virtio_dev)) { + virtio_pci_force_virtio_1(vpci_dev); + } qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } @@ -73,7 +82,6 @@ static void vhost_vsock_pci_instance_init(Object *obj) static const VirtioPCIDeviceTypeInfo vhost_vsock_pci_info = { .base_name = TYPE_VHOST_VSOCK_PCI, .generic_name = "vhost-vsock-pci", - .transitional_name = "vhost-vsock-pci-transitional", .non_transitional_name = "vhost-vsock-pci-non-transitional", .instance_size = sizeof(VHostVSockPCI), .instance_init = vhost_vsock_pci_instance_init, diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 0119516254..68f0a75134 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -818,19 +818,41 @@ static int vhost_dev_set_features(struct vhost_dev *dev, r = dev->vhost_ops->vhost_set_features(dev, features); if (r < 0) { VHOST_OPS_DEBUG("vhost_set_features failed"); + goto out; + } + if (dev->vhost_ops->vhost_set_backend_cap) { + r = dev->vhost_ops->vhost_set_backend_cap(dev); + if (r < 0) { + VHOST_OPS_DEBUG("vhost_set_backend_cap failed"); + goto out; + } } + +out: return r < 0 ? -errno : 0; } static int vhost_dev_set_log(struct vhost_dev *dev, bool enable_log) { int r, i, idx; + hwaddr addr; + r = vhost_dev_set_features(dev, enable_log); if (r < 0) { goto err_features; } for (i = 0; i < dev->nvqs; ++i) { idx = dev->vhost_ops->vhost_get_vq_index(dev, dev->vq_index + i); + addr = virtio_queue_get_desc_addr(dev->vdev, idx); + if (!addr) { + /* + * The queue might not be ready for start. If this + * is the case there is no reason to continue the process. + * The similar logic is used by the vhost_virtqueue_start() + * routine. + */ + continue; + } r = vhost_virtqueue_set_addr(dev, dev->vqs + i, idx, enable_log); if (r < 0) { @@ -861,21 +883,42 @@ static int vhost_migration_log(MemoryListener *listener, bool enable) dev->log_enabled = enable; return 0; } + + r = 0; if (!enable) { r = vhost_dev_set_log(dev, false); if (r < 0) { - return r; + goto check_dev_state; } vhost_log_put(dev, false); } else { vhost_dev_log_resize(dev, vhost_get_log_size(dev)); r = vhost_dev_set_log(dev, true); if (r < 0) { - return r; + goto check_dev_state; } } + +check_dev_state: dev->log_enabled = enable; - return 0; + /* + * vhost-user-* devices could change their state during log + * initialization due to disconnect. So check dev state after + * vhost communication. + */ + if (!dev->started) { + /* + * Since device is in the stopped state, it is okay for + * migration. Return success. + */ + r = 0; + } + if (r) { + /* An error is occured. */ + dev->log_enabled = false; + } + + return r; } static void vhost_log_global_start(MemoryListener *listener) diff --git a/hw/virtio/virtio-iommu-pci.c b/hw/virtio/virtio-iommu-pci.c index 76540e57b1..378f63b210 100644 --- a/hw/virtio/virtio-iommu-pci.c +++ b/hw/virtio/virtio-iommu-pci.c @@ -69,6 +69,7 @@ static void virtio_iommu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) object_property_set_link(OBJECT(dev), "primary-bus", OBJECT(pci_get_bus(&vpci_dev->pci_dev)), &error_abort); + virtio_pci_force_virtio_1(vpci_dev); qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } @@ -98,7 +99,6 @@ static void virtio_iommu_pci_instance_init(Object *obj) static const VirtioPCIDeviceTypeInfo virtio_iommu_pci_info = { .base_name = TYPE_VIRTIO_IOMMU_PCI, .generic_name = "virtio-iommu-pci", - .transitional_name = "virtio-iommu-pci-transitional", .non_transitional_name = "virtio-iommu-pci-non-transitional", .instance_size = sizeof(VirtIOIOMMUPCI), .instance_init = virtio_iommu_pci_instance_init, diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 5d56865e56..21ec63b108 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -801,8 +801,12 @@ static void virtio_iommu_device_unrealize(DeviceState *dev) VirtIOIOMMU *s = VIRTIO_IOMMU(dev); g_hash_table_destroy(s->as_by_busptr); - g_tree_destroy(s->domains); - g_tree_destroy(s->endpoints); + if (s->domains) { + g_tree_destroy(s->domains); + } + if (s->endpoints) { + g_tree_destroy(s->endpoints); + } virtio_delete_queue(s->req_vq); virtio_delete_queue(s->event_vq); diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index 8fbec77ccc..7c8ca9f28b 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -318,6 +318,7 @@ static void virtio_mem_handle_request(VirtIODevice *vdev, VirtQueue *vq) if (iov_to_buf(elem->out_sg, elem->out_num, 0, &req, len) < len) { virtio_error(vdev, "virtio-mem protocol violation: invalid request" " size: %d", len); + virtqueue_detach_element(vq, elem, 0); g_free(elem); return; } @@ -327,6 +328,7 @@ static void virtio_mem_handle_request(VirtIODevice *vdev, VirtQueue *vq) virtio_error(vdev, "virtio-mem protocol violation: not enough space" " for response: %zu", iov_size(elem->in_sg, elem->in_num)); + virtqueue_detach_element(vq, elem, 0); g_free(elem); return; } @@ -348,6 +350,7 @@ static void virtio_mem_handle_request(VirtIODevice *vdev, VirtQueue *vq) default: virtio_error(vdev, "virtio-mem protocol violation: unknown request" " type: %d", type); + virtqueue_detach_element(vq, elem, 0); g_free(elem); return; } diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 02790e3237..36524a5728 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -1597,8 +1597,18 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp) if (legacy) { if (!virtio_legacy_allowed(vdev)) { - error_setg(errp, "device is modern-only, use disable-legacy=on"); - return; + /* + * To avoid migration issues, we allow legacy mode when legacy + * check is disabled in the old machine types (< 5.1). + */ + if (virtio_legacy_check_disabled(vdev)) { + warn_report("device is modern-only, but for backward " + "compatibility legacy is allowed"); + } else { + error_setg(errp, + "device is modern-only, use disable-legacy=on"); + return; + } } if (virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM)) { error_setg(errp, "VIRTIO_F_IOMMU_PLATFORM was supported by" diff --git a/hw/virtio/virtio-pmem-pci.c b/hw/virtio/virtio-pmem-pci.c index 21a457d151..2b2a0b1eae 100644 --- a/hw/virtio/virtio-pmem-pci.c +++ b/hw/virtio/virtio-pmem-pci.c @@ -22,6 +22,7 @@ static void virtio_pmem_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) VirtIOPMEMPCI *pmem_pci = VIRTIO_PMEM_PCI(vpci_dev); DeviceState *vdev = DEVICE(&pmem_pci->vdev); + virtio_pci_force_virtio_1(vpci_dev); qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } diff --git a/hw/virtio/virtio-pmem.c b/hw/virtio/virtio-pmem.c index 1e0c137497..ddb0125901 100644 --- a/hw/virtio/virtio-pmem.c +++ b/hw/virtio/virtio-pmem.c @@ -77,6 +77,7 @@ static void virtio_pmem_flush(VirtIODevice *vdev, VirtQueue *vq) if (req_data->elem.out_num < 1 || req_data->elem.in_num < 1) { virtio_error(vdev, "virtio-pmem request not proper"); + virtqueue_detach_element(vq, (VirtQueueElement *)req_data, 0); g_free(req_data); return; } diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 3a3d012d9f..6f8f865aff 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2963,17 +2963,16 @@ int virtio_set_features(VirtIODevice *vdev, uint64_t val) return -EINVAL; } ret = virtio_set_features_nocheck(vdev, val); - if (!ret) { - if (virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { - /* VIRTIO_RING_F_EVENT_IDX changes the size of the caches. */ - int i; - for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { - if (vdev->vq[i].vring.num != 0) { - virtio_init_region_cache(vdev, i); - } + if (virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { + /* VIRTIO_RING_F_EVENT_IDX changes the size of the caches. */ + int i; + for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { + if (vdev->vq[i].vring.num != 0) { + virtio_init_region_cache(vdev, i); } } - + } + if (!ret) { if (!virtio_device_started(vdev, vdev->status) && !virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) { vdev->start_on_kick = true; @@ -3304,6 +3303,11 @@ bool virtio_legacy_allowed(VirtIODevice *vdev) } } +bool virtio_legacy_check_disabled(VirtIODevice *vdev) +{ + return vdev->disable_legacy_check; +} + hwaddr virtio_queue_get_desc_addr(VirtIODevice *vdev, int n) { return vdev->vq[n].vring.desc; @@ -3713,6 +3717,8 @@ static Property virtio_properties[] = { DEFINE_VIRTIO_COMMON_FEATURES(VirtIODevice, host_features), DEFINE_PROP_BOOL("use-started", VirtIODevice, use_started, true), DEFINE_PROP_BOOL("use-disabled-flag", VirtIODevice, use_disabled_flag, true), + DEFINE_PROP_BOOL("x-disable-legacy-check", VirtIODevice, + disable_legacy_check, false), DEFINE_PROP_END_OF_LIST(), }; |