Browse Source

Merge tag 'mem-2024-09-24' of https://github.com/davidhildenbrand/qemu into staging

Hi,

"Host Memory Backends" and "Memory devices" queue ("mem"):
- Kconfig fix for virtio-based memory devices
- virtio-mem support for suspend+wake-up with plugged memory
- hostmem fix when specifying "merge=off"

# -----BEGIN PGP SIGNATURE-----
#
# iQJFBAABCAAvFiEEG9nKrXNcTDpGDfzKTd4Q9wD/g1oFAmbyikMRHGRhdmlkQHJl
# ZGhhdC5jb20ACgkQTd4Q9wD/g1q6MBAAitNST73Shc+j327WvRLHQDkzkAlIYm+M
# E8NqtDiV11h7A0eNVu+5BkY/ejtY0Fduae3nxIkrHjK20eHHpiNPUp3hBNIhkKs3
# vlSaU8FLGdt58CteMGcLYsP2E32WNNTckaFGwGjDmyUEfk+Gug4r/rJAZXDfuuLV
# 083I0/MuUF+ozPA0c2MrOwhoBPerg3a5aflVpbgPwGNrT9BHMjo62Q5QzG3U7mxr
# HnlLAScSXsYg2z+d5XLXkKLAiZ4C7UN4vfUAOZwqkfs7IFUTtFO/ev6e7VZI747n
# XhAqOAKzLqPu7tBPZJIC6jwZAUIv5yM0/v5qhVvVVdu7H0ZMtSCXyvCVtnT25Rsn
# yiA+XvCOb7yQ3hRbBIi60IzjNYfWbvw+oTVIDfXkG35TeNf4ZdjWtAiUmw9s5U9Q
# z0tINsD7VlSkbh5h3PkFw1+xagIuJAVkp673HHTtQsg+xgYK2ur5jhhWJdJlnpzA
# 77CAu07UaqU39ssnC2zeGG1eNRA4uzjwQtREzqH2jMfkw/7UuUeXMF+v/fEuLn6w
# JneSMq/a0gmD42HNae0Y40cn2Akfj6+wFu1rW3djF8F6TeLUSssQhbQSHCMwGoOg
# qX7O/3SeSRzlnp3Zyx9Tr7s+BkMz0EGGDe17GQwTQUX2t5wR5iXoGqpKZgOBA8En
# 6uUIcjBUckc=
# =PExj
# -----END PGP SIGNATURE-----
# gpg: Signature made Tue 24 Sep 2024 10:45:39 BST
# gpg:                using RSA key 1BD9CAAD735C4C3A460DFCCA4DDE10F700FF835A
# gpg:                issuer "david@redhat.com"
# gpg: Good signature from "David Hildenbrand <david@redhat.com>" [marginal]
# gpg:                 aka "David Hildenbrand <davidhildenbrand@gmail.com>" [full]
# gpg:                 aka "David Hildenbrand <hildenbr@in.tum.de>" [unknown]
# Primary key fingerprint: 1BD9 CAAD 735C 4C3A 460D  FCCA 4DDE 10F7 00FF 835A

* tag 'mem-2024-09-24' of https://github.com/davidhildenbrand/qemu:
  hostmem: Apply merge property after the memory region is initialized
  virtio-mem: Add support for suspend+wake-up with plugged memory
  virtio-mem: Use new Resettable framework instead of LegacyReset
  reset: Add RESET_TYPE_WAKEUP
  reset: Use ResetType for qemu_devices_reset() and MachineClass::reset()
  virtio: kconfig: memory devices are PCI only

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Peter Maydell 10 months ago
parent
commit
e10cd93872

+ 1 - 1
backends/hostmem.c

@@ -178,7 +178,7 @@ static void host_memory_backend_set_merge(Object *obj, bool value, Error **errp)
         return;
         return;
     }
     }
 
 
-    if (!host_memory_backend_mr_inited(backend) &&
+    if (host_memory_backend_mr_inited(backend) &&
         value != backend->merge) {
         value != backend->merge) {
         void *ptr = memory_region_get_ram_ptr(&backend->mr);
         void *ptr = memory_region_get_ram_ptr(&backend->mr);
         uint64_t sz = memory_region_size(&backend->mr);
         uint64_t sz = memory_region_size(&backend->mr);

+ 11 - 1
docs/devel/reset.rst

@@ -44,6 +44,17 @@ The Resettable interface handles reset types with an enum ``ResetType``:
   value on each cold reset, such as RNG seed information, and which they
   value on each cold reset, such as RNG seed information, and which they
   must not reinitialize on a snapshot-load reset.
   must not reinitialize on a snapshot-load reset.
 
 
+``RESET_TYPE_WAKEUP``
+  If the machine supports waking up from a suspended state and needs to reset
+  its devices during wake-up (from the ``MachineClass::wakeup()`` method), this
+  reset type should be used for such a request. Devices can utilize this reset
+  type to differentiate the reset requested during machine wake-up from other
+  reset requests. For example, RAM content must not be lost during wake-up, and
+  memory devices like virtio-mem that provide additional RAM must not reset
+  such state during wake-ups, but might do so during cold resets. However, this
+  reset type should not be used for wake-up detection, as not every machine
+  type issues a device reset request during wake-up.
+
 ``RESET_TYPE_S390_CPU_NORMAL``
 ``RESET_TYPE_S390_CPU_NORMAL``
   This is only used for S390 CPU objects; it clears interrupts, stops
   This is only used for S390 CPU objects; it clears interrupts, stops
   processing, and clears the TLB, but does not touch register contents.
   processing, and clears the TLB, but does not touch register contents.
@@ -53,7 +64,6 @@ The Resettable interface handles reset types with an enum ``ResetType``:
   ``RESET_TYPE_S390_CPU_NORMAL`` does and also clears the PSW, prefix,
   ``RESET_TYPE_S390_CPU_NORMAL`` does and also clears the PSW, prefix,
   FPC, timer and control registers. It does not touch gprs, fprs or acrs.
   FPC, timer and control registers. It does not touch gprs, fprs or acrs.
 
 
-
 Devices which implement reset methods must treat any unknown ``ResetType``
 Devices which implement reset methods must treat any unknown ``ResetType``
 as equivalent to ``RESET_TYPE_COLD``; this will reduce the amount of
 as equivalent to ``RESET_TYPE_COLD``; this will reduce the amount of
 existing code we need to change if we add more types in future.
 existing code we need to change if we add more types in future.

+ 2 - 2
hw/arm/aspeed.c

@@ -1529,12 +1529,12 @@ static void aspeed_machine_bletchley_class_init(ObjectClass *oc, void *data)
     aspeed_machine_class_init_cpus_defaults(mc);
     aspeed_machine_class_init_cpus_defaults(mc);
 }
 }
 
 
-static void fby35_reset(MachineState *state, ShutdownCause reason)
+static void fby35_reset(MachineState *state, ResetType type)
 {
 {
     AspeedMachineState *bmc = ASPEED_MACHINE(state);
     AspeedMachineState *bmc = ASPEED_MACHINE(state);
     AspeedGPIOState *gpio = &bmc->soc->gpio;
     AspeedGPIOState *gpio = &bmc->soc->gpio;
 
 
-    qemu_devices_reset(reason);
+    qemu_devices_reset(type);
 
 
     /* Board ID: 7 (Class-1, 4 slots) */
     /* Board ID: 7 (Class-1, 4 slots) */
     object_property_set_bool(OBJECT(gpio), "gpioV4", true, &error_fatal);
     object_property_set_bool(OBJECT(gpio), "gpioV4", true, &error_fatal);

+ 2 - 2
hw/arm/mps2-tz.c

@@ -1254,7 +1254,7 @@ static void mps2_set_remap(Object *obj, const char *value, Error **errp)
     }
     }
 }
 }
 
 
-static void mps2_machine_reset(MachineState *machine, ShutdownCause reason)
+static void mps2_machine_reset(MachineState *machine, ResetType type)
 {
 {
     MPS2TZMachineState *mms = MPS2TZ_MACHINE(machine);
     MPS2TZMachineState *mms = MPS2TZ_MACHINE(machine);
 
 
@@ -1264,7 +1264,7 @@ static void mps2_machine_reset(MachineState *machine, ShutdownCause reason)
      * reset see the correct mapping.
      * reset see the correct mapping.
      */
      */
     remap_memory(mms, mms->remap);
     remap_memory(mms, mms->remap);
-    qemu_devices_reset(reason);
+    qemu_devices_reset(type);
 }
 }
 
 
 static void mps2tz_class_init(ObjectClass *oc, void *data)
 static void mps2tz_class_init(ObjectClass *oc, void *data)

+ 1 - 4
hw/core/reset.c

@@ -170,11 +170,8 @@ void qemu_unregister_resettable(Object *obj)
     resettable_container_remove(get_root_reset_container(), obj);
     resettable_container_remove(get_root_reset_container(), obj);
 }
 }
 
 
-void qemu_devices_reset(ShutdownCause reason)
+void qemu_devices_reset(ResetType type)
 {
 {
-    ResetType type = (reason == SHUTDOWN_CAUSE_SNAPSHOT_LOAD) ?
-        RESET_TYPE_SNAPSHOT_LOAD : RESET_TYPE_COLD;
-
     /* Reset the simulation */
     /* Reset the simulation */
     resettable_reset(OBJECT(get_root_reset_container()), type);
     resettable_reset(OBJECT(get_root_reset_container()), type);
 }
 }

+ 2 - 2
hw/hppa/machine.c

@@ -642,12 +642,12 @@ static void machine_HP_C3700_init(MachineState *machine)
     machine_HP_common_init_tail(machine, pci_bus, translate);
     machine_HP_common_init_tail(machine, pci_bus, translate);
 }
 }
 
 
-static void hppa_machine_reset(MachineState *ms, ShutdownCause reason)
+static void hppa_machine_reset(MachineState *ms, ResetType type)
 {
 {
     unsigned int smp_cpus = ms->smp.cpus;
     unsigned int smp_cpus = ms->smp.cpus;
     int i;
     int i;
 
 
-    qemu_devices_reset(reason);
+    qemu_devices_reset(type);
 
 
     /* Start all CPUs at the firmware entry point.
     /* Start all CPUs at the firmware entry point.
      *  Monarch CPU will initialize firmware, secondary CPUs
      *  Monarch CPU will initialize firmware, secondary CPUs

+ 2 - 2
hw/i386/microvm.c

@@ -462,7 +462,7 @@ static void microvm_machine_state_init(MachineState *machine)
     microvm_devices_init(mms);
     microvm_devices_init(mms);
 }
 }
 
 
-static void microvm_machine_reset(MachineState *machine, ShutdownCause reason)
+static void microvm_machine_reset(MachineState *machine, ResetType type)
 {
 {
     MicrovmMachineState *mms = MICROVM_MACHINE(machine);
     MicrovmMachineState *mms = MICROVM_MACHINE(machine);
     CPUState *cs;
     CPUState *cs;
@@ -475,7 +475,7 @@ static void microvm_machine_reset(MachineState *machine, ShutdownCause reason)
         mms->kernel_cmdline_fixed = true;
         mms->kernel_cmdline_fixed = true;
     }
     }
 
 
-    qemu_devices_reset(reason);
+    qemu_devices_reset(type);
 
 
     CPU_FOREACH(cs) {
     CPU_FOREACH(cs) {
         cpu = X86_CPU(cs);
         cpu = X86_CPU(cs);

+ 3 - 3
hw/i386/pc.c

@@ -1712,12 +1712,12 @@ static void pc_machine_initfn(Object *obj)
     qemu_add_machine_init_done_notifier(&pcms->machine_done);
     qemu_add_machine_init_done_notifier(&pcms->machine_done);
 }
 }
 
 
-static void pc_machine_reset(MachineState *machine, ShutdownCause reason)
+static void pc_machine_reset(MachineState *machine, ResetType type)
 {
 {
     CPUState *cs;
     CPUState *cs;
     X86CPU *cpu;
     X86CPU *cpu;
 
 
-    qemu_devices_reset(reason);
+    qemu_devices_reset(type);
 
 
     /* Reset APIC after devices have been reset to cancel
     /* Reset APIC after devices have been reset to cancel
      * any changes that qemu_devices_reset() might have done.
      * any changes that qemu_devices_reset() might have done.
@@ -1732,7 +1732,7 @@ static void pc_machine_reset(MachineState *machine, ShutdownCause reason)
 static void pc_machine_wakeup(MachineState *machine)
 static void pc_machine_wakeup(MachineState *machine)
 {
 {
     cpu_synchronize_all_states();
     cpu_synchronize_all_states();
-    pc_machine_reset(machine, SHUTDOWN_CAUSE_NONE);
+    pc_machine_reset(machine, RESET_TYPE_WAKEUP);
     cpu_synchronize_all_post_reset();
     cpu_synchronize_all_post_reset();
 }
 }
 
 

+ 2 - 2
hw/ppc/pegasos2.c

@@ -291,14 +291,14 @@ static void pegasos2_superio_write(uint8_t addr, uint8_t val)
     cpu_physical_memory_write(PCI1_IO_BASE + 0x3f1, &val, 1);
     cpu_physical_memory_write(PCI1_IO_BASE + 0x3f1, &val, 1);
 }
 }
 
 
-static void pegasos2_machine_reset(MachineState *machine, ShutdownCause reason)
+static void pegasos2_machine_reset(MachineState *machine, ResetType type)
 {
 {
     Pegasos2MachineState *pm = PEGASOS2_MACHINE(machine);
     Pegasos2MachineState *pm = PEGASOS2_MACHINE(machine);
     void *fdt;
     void *fdt;
     uint64_t d[2];
     uint64_t d[2];
     int sz;
     int sz;
 
 
-    qemu_devices_reset(reason);
+    qemu_devices_reset(type);
     if (!pm->vof) {
     if (!pm->vof) {
         return; /* Firmware should set up machine so nothing to do */
         return; /* Firmware should set up machine so nothing to do */
     }
     }

+ 2 - 2
hw/ppc/pnv.c

@@ -709,13 +709,13 @@ static void pnv_powerdown_notify(Notifier *n, void *opaque)
     }
     }
 }
 }
 
 
-static void pnv_reset(MachineState *machine, ShutdownCause reason)
+static void pnv_reset(MachineState *machine, ResetType type)
 {
 {
     PnvMachineState *pnv = PNV_MACHINE(machine);
     PnvMachineState *pnv = PNV_MACHINE(machine);
     IPMIBmc *bmc;
     IPMIBmc *bmc;
     void *fdt;
     void *fdt;
 
 
-    qemu_devices_reset(reason);
+    qemu_devices_reset(type);
 
 
     /*
     /*
      * The machine should provide by default an internal BMC simulator.
      * The machine should provide by default an internal BMC simulator.

+ 3 - 3
hw/ppc/spapr.c

@@ -1725,7 +1725,7 @@ void spapr_check_mmu_mode(bool guest_radix)
     }
     }
 }
 }
 
 
-static void spapr_machine_reset(MachineState *machine, ShutdownCause reason)
+static void spapr_machine_reset(MachineState *machine, ResetType type)
 {
 {
     SpaprMachineState *spapr = SPAPR_MACHINE(machine);
     SpaprMachineState *spapr = SPAPR_MACHINE(machine);
     PowerPCCPU *first_ppc_cpu;
     PowerPCCPU *first_ppc_cpu;
@@ -1733,7 +1733,7 @@ static void spapr_machine_reset(MachineState *machine, ShutdownCause reason)
     void *fdt;
     void *fdt;
     int rc;
     int rc;
 
 
-    if (reason != SHUTDOWN_CAUSE_SNAPSHOT_LOAD) {
+    if (type != RESET_TYPE_SNAPSHOT_LOAD) {
         /*
         /*
          * Record-replay snapshot load must not consume random, this was
          * Record-replay snapshot load must not consume random, this was
          * already replayed from initial machine reset.
          * already replayed from initial machine reset.
@@ -1762,7 +1762,7 @@ static void spapr_machine_reset(MachineState *machine, ShutdownCause reason)
         spapr_setup_hpt(spapr);
         spapr_setup_hpt(spapr);
     }
     }
 
 
-    qemu_devices_reset(reason);
+    qemu_devices_reset(type);
 
 
     spapr_ovec_cleanup(spapr->ov5_cas);
     spapr_ovec_cleanup(spapr->ov5_cas);
     spapr->ov5_cas = spapr_ovec_new();
     spapr->ov5_cas = spapr_ovec_new();

+ 2 - 2
hw/s390x/s390-virtio-ccw.c

@@ -440,7 +440,7 @@ static void s390_pv_prepare_reset(S390CcwMachineState *ms)
     s390_pv_prep_reset();
     s390_pv_prep_reset();
 }
 }
 
 
-static void s390_machine_reset(MachineState *machine, ShutdownCause reason)
+static void s390_machine_reset(MachineState *machine, ResetType type)
 {
 {
     S390CcwMachineState *ms = S390_CCW_MACHINE(machine);
     S390CcwMachineState *ms = S390_CCW_MACHINE(machine);
     enum s390_reset reset_type;
     enum s390_reset reset_type;
@@ -472,7 +472,7 @@ static void s390_machine_reset(MachineState *machine, ShutdownCause reason)
          * Device reset includes CPU clear resets so this has to be
          * Device reset includes CPU clear resets so this has to be
          * done AFTER the unprotect call above.
          * done AFTER the unprotect call above.
          */
          */
-        qemu_devices_reset(reason);
+        qemu_devices_reset(type);
         s390_crypto_reset();
         s390_crypto_reset();
 
 
         /* configure and start the ipl CPU only */
         /* configure and start the ipl CPU only */

+ 11 - 0
hw/virtio/Kconfig

@@ -16,6 +16,7 @@ config VIRTIO_PCI
     default y if PCI_DEVICES
     default y if PCI_DEVICES
     depends on PCI
     depends on PCI
     select VIRTIO
     select VIRTIO
+    select VIRTIO_MD_SUPPORTED
 
 
 config VIRTIO_MMIO
 config VIRTIO_MMIO
     bool
     bool
@@ -35,10 +36,17 @@ config VIRTIO_CRYPTO
     default y
     default y
     depends on VIRTIO
     depends on VIRTIO
 
 
+# not all virtio transports support memory devices; if none does,
+# no need to include the code
+config VIRTIO_MD_SUPPORTED
+    bool
+
 config VIRTIO_MD
 config VIRTIO_MD
     bool
     bool
+    depends on VIRTIO_MD_SUPPORTED
     select MEM_DEVICE
     select MEM_DEVICE
 
 
+# selected by the board if it has the required support code
 config VIRTIO_PMEM_SUPPORTED
 config VIRTIO_PMEM_SUPPORTED
     bool
     bool
 
 
@@ -46,9 +54,11 @@ config VIRTIO_PMEM
     bool
     bool
     default y
     default y
     depends on VIRTIO
     depends on VIRTIO
+    depends on VIRTIO_MD_SUPPORTED
     depends on VIRTIO_PMEM_SUPPORTED
     depends on VIRTIO_PMEM_SUPPORTED
     select VIRTIO_MD
     select VIRTIO_MD
 
 
+# selected by the board if it has the required support code
 config VIRTIO_MEM_SUPPORTED
 config VIRTIO_MEM_SUPPORTED
     bool
     bool
 
 
@@ -57,6 +67,7 @@ config VIRTIO_MEM
     default y
     default y
     depends on VIRTIO
     depends on VIRTIO
     depends on LINUX
     depends on LINUX
+    depends on VIRTIO_MD_SUPPORTED
     depends on VIRTIO_MEM_SUPPORTED
     depends on VIRTIO_MEM_SUPPORTED
     select VIRTIO_MD
     select VIRTIO_MD
 
 

+ 34 - 14
hw/virtio/virtio-mem.c

@@ -890,6 +890,9 @@ static uint64_t virtio_mem_get_features(VirtIODevice *vdev, uint64_t features,
     if (vmem->unplugged_inaccessible == ON_OFF_AUTO_ON) {
     if (vmem->unplugged_inaccessible == ON_OFF_AUTO_ON) {
         virtio_add_feature(&features, VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE);
         virtio_add_feature(&features, VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE);
     }
     }
+    if (qemu_wakeup_suspend_enabled()) {
+        virtio_add_feature(&features, VIRTIO_MEM_F_PERSISTENT_SUSPEND);
+    }
     return features;
     return features;
 }
 }
 
 
@@ -902,18 +905,6 @@ static int virtio_mem_validate_features(VirtIODevice *vdev)
     return 0;
     return 0;
 }
 }
 
 
-static void virtio_mem_system_reset(void *opaque)
-{
-    VirtIOMEM *vmem = VIRTIO_MEM(opaque);
-
-    /*
-     * During usual resets, we will unplug all memory and shrink the usable
-     * region size. This is, however, not possible in all scenarios. Then,
-     * the guest has to deal with this manually (VIRTIO_MEM_REQ_UNPLUG_ALL).
-     */
-    virtio_mem_unplug_all(vmem);
-}
-
 static void virtio_mem_prepare_mr(VirtIOMEM *vmem)
 static void virtio_mem_prepare_mr(VirtIOMEM *vmem)
 {
 {
     const uint64_t region_size = memory_region_size(&vmem->memdev->mr);
     const uint64_t region_size = memory_region_size(&vmem->memdev->mr);
@@ -1130,7 +1121,7 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp)
         vmstate_register_any(VMSTATE_IF(vmem),
         vmstate_register_any(VMSTATE_IF(vmem),
                              &vmstate_virtio_mem_device_early, vmem);
                              &vmstate_virtio_mem_device_early, vmem);
     }
     }
-    qemu_register_reset(virtio_mem_system_reset, vmem);
+    qemu_register_resettable(OBJECT(vmem));
 
 
     /*
     /*
      * Set ourselves as RamDiscardManager before the plug handler maps the
      * Set ourselves as RamDiscardManager before the plug handler maps the
@@ -1150,7 +1141,7 @@ static void virtio_mem_device_unrealize(DeviceState *dev)
      * found via an address space anymore. Unset ourselves.
      * found via an address space anymore. Unset ourselves.
      */
      */
     memory_region_set_ram_discard_manager(&vmem->memdev->mr, NULL);
     memory_region_set_ram_discard_manager(&vmem->memdev->mr, NULL);
-    qemu_unregister_reset(virtio_mem_system_reset, vmem);
+    qemu_unregister_resettable(OBJECT(vmem));
     if (vmem->early_migration) {
     if (vmem->early_migration) {
         vmstate_unregister(VMSTATE_IF(vmem), &vmstate_virtio_mem_device_early,
         vmstate_unregister(VMSTATE_IF(vmem), &vmstate_virtio_mem_device_early,
                            vmem);
                            vmem);
@@ -1850,12 +1841,38 @@ static void virtio_mem_unplug_request_check(VirtIOMEM *vmem, Error **errp)
     }
     }
 }
 }
 
 
+static ResettableState *virtio_mem_get_reset_state(Object *obj)
+{
+    VirtIOMEM *vmem = VIRTIO_MEM(obj);
+    return &vmem->reset_state;
+}
+
+static void virtio_mem_system_reset_hold(Object *obj, ResetType type)
+{
+    VirtIOMEM *vmem = VIRTIO_MEM(obj);
+
+    /*
+     * When waking up from standby/suspend-to-ram, do not unplug any memory.
+     */
+    if (type == RESET_TYPE_WAKEUP) {
+        return;
+    }
+
+    /*
+     * During usual resets, we will unplug all memory and shrink the usable
+     * region size. This is, however, not possible in all scenarios. Then,
+     * the guest has to deal with this manually (VIRTIO_MEM_REQ_UNPLUG_ALL).
+     */
+    virtio_mem_unplug_all(vmem);
+}
+
 static void virtio_mem_class_init(ObjectClass *klass, void *data)
 static void virtio_mem_class_init(ObjectClass *klass, void *data)
 {
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
     DeviceClass *dc = DEVICE_CLASS(klass);
     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
     VirtIOMEMClass *vmc = VIRTIO_MEM_CLASS(klass);
     VirtIOMEMClass *vmc = VIRTIO_MEM_CLASS(klass);
     RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_CLASS(klass);
     RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_CLASS(klass);
+    ResettableClass *rc = RESETTABLE_CLASS(klass);
 
 
     device_class_set_props(dc, virtio_mem_properties);
     device_class_set_props(dc, virtio_mem_properties);
     dc->vmsd = &vmstate_virtio_mem;
     dc->vmsd = &vmstate_virtio_mem;
@@ -1882,6 +1899,9 @@ static void virtio_mem_class_init(ObjectClass *klass, void *data)
     rdmc->replay_discarded = virtio_mem_rdm_replay_discarded;
     rdmc->replay_discarded = virtio_mem_rdm_replay_discarded;
     rdmc->register_listener = virtio_mem_rdm_register_listener;
     rdmc->register_listener = virtio_mem_rdm_register_listener;
     rdmc->unregister_listener = virtio_mem_rdm_unregister_listener;
     rdmc->unregister_listener = virtio_mem_rdm_unregister_listener;
+
+    rc->get_state = virtio_mem_get_reset_state;
+    rc->phases.hold = virtio_mem_system_reset_hold;
 }
 }
 
 
 static const TypeInfo virtio_mem_info = {
 static const TypeInfo virtio_mem_info = {

+ 3 - 0
hw/virtio/virtio-qmp.c

@@ -450,6 +450,9 @@ static const qmp_virtio_feature_map_t virtio_mem_feature_map[] = {
     FEATURE_ENTRY(VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE, \
     FEATURE_ENTRY(VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE, \
             "VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE: Unplugged memory cannot be "
             "VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE: Unplugged memory cannot be "
             "accessed"),
             "accessed"),
+    FEATURE_ENTRY(VIRTIO_MEM_F_PERSISTENT_SUSPEND, \
+            "VIRTIO_MEM_F_PERSISTENT_SUSPND: Plugged memory will remain "
+            "plugged when suspending+resuming"),
     { -1, "" }
     { -1, "" }
 };
 };
 #endif
 #endif

+ 2 - 1
include/hw/boards.h

@@ -10,6 +10,7 @@
 #include "qemu/module.h"
 #include "qemu/module.h"
 #include "qom/object.h"
 #include "qom/object.h"
 #include "hw/core/cpu.h"
 #include "hw/core/cpu.h"
+#include "hw/resettable.h"
 
 
 #define TYPE_MACHINE_SUFFIX "-machine"
 #define TYPE_MACHINE_SUFFIX "-machine"
 
 
@@ -257,7 +258,7 @@ struct MachineClass {
     const char *deprecation_reason;
     const char *deprecation_reason;
 
 
     void (*init)(MachineState *state);
     void (*init)(MachineState *state);
-    void (*reset)(MachineState *state, ShutdownCause reason);
+    void (*reset)(MachineState *state, ResetType type);
     void (*wakeup)(MachineState *state);
     void (*wakeup)(MachineState *state);
     int (*kvm_type)(MachineState *machine, const char *arg);
     int (*kvm_type)(MachineState *machine, const char *arg);
     int (*hvf_get_physical_address_range)(MachineState *machine);
     int (*hvf_get_physical_address_range)(MachineState *machine);

+ 2 - 0
include/hw/resettable.h

@@ -29,6 +29,7 @@ typedef struct ResettableState ResettableState;
  * Types of reset.
  * Types of reset.
  *
  *
  * + Cold: reset resulting from a power cycle of the object.
  * + Cold: reset resulting from a power cycle of the object.
+ * + Wakeup: reset resulting from a wake-up from a suspended state.
  *
  *
  * TODO: Support has to be added to handle more types. In particular,
  * TODO: Support has to be added to handle more types. In particular,
  * ResettableState structure needs to be expanded.
  * ResettableState structure needs to be expanded.
@@ -36,6 +37,7 @@ typedef struct ResettableState ResettableState;
 typedef enum ResetType {
 typedef enum ResetType {
     RESET_TYPE_COLD,
     RESET_TYPE_COLD,
     RESET_TYPE_SNAPSHOT_LOAD,
     RESET_TYPE_SNAPSHOT_LOAD,
+    RESET_TYPE_WAKEUP,
     RESET_TYPE_S390_CPU_INITIAL,
     RESET_TYPE_S390_CPU_INITIAL,
     RESET_TYPE_S390_CPU_NORMAL,
     RESET_TYPE_S390_CPU_NORMAL,
 } ResetType;
 } ResetType;

+ 4 - 0
include/hw/virtio/virtio-mem.h

@@ -14,6 +14,7 @@
 #define HW_VIRTIO_MEM_H
 #define HW_VIRTIO_MEM_H
 
 
 #include "standard-headers/linux/virtio_mem.h"
 #include "standard-headers/linux/virtio_mem.h"
+#include "hw/resettable.h"
 #include "hw/virtio/virtio.h"
 #include "hw/virtio/virtio.h"
 #include "qapi/qapi-types-misc.h"
 #include "qapi/qapi-types-misc.h"
 #include "sysemu/hostmem.h"
 #include "sysemu/hostmem.h"
@@ -115,6 +116,9 @@ struct VirtIOMEM {
 
 
     /* listeners to notify on plug/unplug activity. */
     /* listeners to notify on plug/unplug activity. */
     QLIST_HEAD(, RamDiscardListener) rdl_list;
     QLIST_HEAD(, RamDiscardListener) rdl_list;
+
+    /* State of the resettable container */
+    ResettableState reset_state;
 };
 };
 
 
 struct VirtIOMEMClass {
 struct VirtIOMEMClass {

+ 3 - 2
include/sysemu/reset.h

@@ -27,6 +27,7 @@
 #ifndef QEMU_SYSEMU_RESET_H
 #ifndef QEMU_SYSEMU_RESET_H
 #define QEMU_SYSEMU_RESET_H
 #define QEMU_SYSEMU_RESET_H
 
 
+#include "hw/resettable.h"
 #include "qapi/qapi-events-run-state.h"
 #include "qapi/qapi-events-run-state.h"
 
 
 typedef void QEMUResetHandler(void *opaque);
 typedef void QEMUResetHandler(void *opaque);
@@ -110,7 +111,7 @@ void qemu_unregister_reset(QEMUResetHandler *func, void *opaque);
 
 
 /**
 /**
  * qemu_devices_reset: Perform a complete system reset
  * qemu_devices_reset: Perform a complete system reset
- * @reason: reason for the reset
+ * @reason: type of the reset
  *
  *
  * This function performs the low-level work needed to do a complete reset
  * This function performs the low-level work needed to do a complete reset
  * of the system (calling all the callbacks registered with
  * of the system (calling all the callbacks registered with
@@ -121,6 +122,6 @@ void qemu_unregister_reset(QEMUResetHandler *func, void *opaque);
  * If you want to trigger a system reset from, for instance, a device
  * If you want to trigger a system reset from, for instance, a device
  * model, don't use this function. Use qemu_system_reset_request().
  * model, don't use this function. Use qemu_system_reset_request().
  */
  */
-void qemu_devices_reset(ShutdownCause reason);
+void qemu_devices_reset(ResetType type);
 
 
 #endif
 #endif

+ 11 - 2
system/runstate.c

@@ -32,6 +32,7 @@
 #include "exec/cpu-common.h"
 #include "exec/cpu-common.h"
 #include "gdbstub/syscalls.h"
 #include "gdbstub/syscalls.h"
 #include "hw/boards.h"
 #include "hw/boards.h"
+#include "hw/resettable.h"
 #include "migration/misc.h"
 #include "migration/misc.h"
 #include "migration/postcopy-ram.h"
 #include "migration/postcopy-ram.h"
 #include "monitor/monitor.h"
 #include "monitor/monitor.h"
@@ -507,15 +508,23 @@ static int qemu_debug_requested(void)
 void qemu_system_reset(ShutdownCause reason)
 void qemu_system_reset(ShutdownCause reason)
 {
 {
     MachineClass *mc;
     MachineClass *mc;
+    ResetType type;
 
 
     mc = current_machine ? MACHINE_GET_CLASS(current_machine) : NULL;
     mc = current_machine ? MACHINE_GET_CLASS(current_machine) : NULL;
 
 
     cpu_synchronize_all_states();
     cpu_synchronize_all_states();
 
 
+    switch (reason) {
+    case SHUTDOWN_CAUSE_SNAPSHOT_LOAD:
+        type = RESET_TYPE_SNAPSHOT_LOAD;
+        break;
+    default:
+        type = RESET_TYPE_COLD;
+    }
     if (mc && mc->reset) {
     if (mc && mc->reset) {
-        mc->reset(current_machine, reason);
+        mc->reset(current_machine, type);
     } else {
     } else {
-        qemu_devices_reset(reason);
+        qemu_devices_reset(type);
     }
     }
     switch (reason) {
     switch (reason) {
     case SHUTDOWN_CAUSE_NONE:
     case SHUTDOWN_CAUSE_NONE: