* [PATCH v4 00/21] virtio-mem: Paravirtualized memory hot(un)plug
@ 2020-06-10 11:53 David Hildenbrand
2020-06-10 11:53 ` [PATCH v4 01/21] exec: Introduce ram_block_discard_(disable|require)() David Hildenbrand
` (21 more replies)
0 siblings, 22 replies; 34+ messages in thread
From: David Hildenbrand @ 2020-06-10 11:53 UTC (permalink / raw)
To: qemu-devel
Cc: kvm, qemu-s390x, Richard Henderson, Paolo Bonzini,
Dr . David Alan Gilbert, Eduardo Habkost, Michael S . Tsirkin,
David Hildenbrand, teawater, Pankaj Gupta, Alex Shi,
Alex Williamson, Christian Borntraeger, Cornelia Huck, Eric Blake,
Eric Farman, Hailiang Zhang, Halil Pasic, Igor Mammedov,
Janosch Frank, Juan Quintela, Keith Busch, Lukas Straub,
Marcel Apfelbaum, Markus Armbruster, Peter Maydell, Pierre Morel,
qemu-arm@nongnu.org, Sergio Lopez, Tony Krowiak
This is the very basic, initial version of virtio-mem. More info on
virtio-mem in general can be found in the Linux kernel driver v2 posting
[1] and in patch #10. The Linux driver is currently on its way upstream.
This series is based on [3]:
"[PATCH v1] pc: Support coldplugging of virtio-pmem-pci devices on all
buses"
And [4]:
"[PATCH v2] hmp: Make json format optional for qom-set"
The patches can be found at:
https://github.com/davidhildenbrand/qemu.git virtio-mem-v4
"The basic idea of virtio-mem is to provide a flexible,
cross-architecture memory hot(un)plug solution that avoids many limitations
imposed by existing technologies, architectures, and interfaces."
There are a lot of addons in the works (esp. protection of unplugged
memory, better hugepage support (esp. when reading unplugged memory),
resizeable memory backends, support for more architectures, ...), this is
the very basic version to get the ball rolling.
The first 8 patches make sure we don't have any sudden surprises e.g., if
somebody tries to pin all memory in RAM blocks, resulting in a higher
memory consumption than desired. The remaining patches add basic virtio-mem
along with support for x86-64. The last patch indicates to the guest OS
the maximum possible PFN using ACPI SRAT, such that Linux can properly
enable the swiotlb when booting only with DMA memory.
[1] https://lkml.kernel.org/r/20200311171422.10484-1-david@redhat.com
[2] https://lkml.kernel.org/r/20200507140139.17083-1-david@redhat.com
[3] https://lkml.kernel.org/r/20200525084511.51379-1-david@redhat.com
[3] https://lkml.kernel.org/r/20200610075153.33892-1-david@redhat.com
Based-on: <20200525084511.51379-1-david@redhat.com>
Based-on: <20200610075153.33892-1-david@redhat.com>
Cc: teawater <teawaterz@linux.alibaba.com>
Cc: Pankaj Gupta <pankaj.gupta.linux@gmail.com>
v3 -> v4
- Adapt to virtio-mem config layout change (block size now is 64bit)
- Added "numa: Auto-enable NUMA when any memory devices are possible"
v2 -> v3:
- Rebased on upstream/[3]
- "virtio-mem: Exclude unplugged memory during migration"
-- Added
- "virtio-mem: Paravirtualized memory hot(un)plug"
-- Simplify bitmap operations, find consecutive areas
-- Tweak error messages
-- Reshuffle some checks
-- Minor cleanups
- "accel/kvm: Convert to ram_block_discard_disable()"
- "target/i386: sev: Use ram_block_discard_disable()"
-- Keep asserts clean of functional things
v1 -> v2:
- Rebased to object_property_*() changes
- "exec: Introduce ram_block_discard_(disable|require)()"
-- Change the function names and rephrase/add comments
- "virtio-balloon: Rip out qemu_balloon_inhibit()"
-- Add and use "migration_in_incoming_postcopy()"
- "migration/rdma: Use ram_block_discard_disable()"
-- Add a comment regarding pin_all vs. !pin_all
- "virtio-mem: Paravirtualized memory hot(un)plug"
-- Replace virtio_mem_discard_inhibited() by
migration_in_incoming_postcopy()
-- Drop some asserts
-- Drop virtio_mem_bad_request(), use virtio_error() directly, printing
more information
-- Replace "Note: Discarding should never fail ..." comments by
error_report()
-- Replace virtio_stw_p() by cpu_to_le16()
-- Drop migration_addr and migration_block_size
-- Minor cleanups
- "linux-headers: update to contain virtio-mem"
-- Updated to latest v4 in Linux
- General changes
-- Fixup the users of the renamed ram_block_discard_(disable|require)
-- Use "X: cannot disable RAM discard"-styled error messages
- Added
-- "virtio-mem: Migration sanity checks"
-- "virtio-mem: Add trace events"
David Hildenbrand (21):
exec: Introduce ram_block_discard_(disable|require)()
vfio: Convert to ram_block_discard_disable()
accel/kvm: Convert to ram_block_discard_disable()
s390x/pv: Convert to ram_block_discard_disable()
virtio-balloon: Rip out qemu_balloon_inhibit()
target/i386: sev: Use ram_block_discard_disable()
migration/rdma: Use ram_block_discard_disable()
migration/colo: Use ram_block_discard_disable()
linux-headers: update to contain virtio-mem
virtio-mem: Paravirtualized memory hot(un)plug
virtio-pci: Proxy for virtio-mem
MAINTAINERS: Add myself as virtio-mem maintainer
hmp: Handle virtio-mem when printing memory device info
numa: Handle virtio-mem in NUMA stats
pc: Support for virtio-mem-pci
virtio-mem: Allow notifiers for size changes
virtio-pci: Send qapi events when the virtio-mem size changes
virtio-mem: Migration sanity checks
virtio-mem: Add trace events
virtio-mem: Exclude unplugged memory during migration
numa: Auto-enable NUMA when any memory devices are possible
MAINTAINERS | 8 +
accel/kvm/kvm-all.c | 4 +-
balloon.c | 17 -
exec.c | 52 ++
hw/arm/virt.c | 2 +
hw/core/numa.c | 17 +-
hw/i386/Kconfig | 1 +
hw/i386/microvm.c | 1 +
hw/i386/pc.c | 50 +-
hw/i386/pc_piix.c | 1 +
hw/i386/pc_q35.c | 1 +
hw/s390x/s390-virtio-ccw.c | 22 +-
hw/vfio/ap.c | 10 +-
hw/vfio/ccw.c | 11 +-
hw/vfio/common.c | 53 +-
hw/vfio/pci.c | 6 +-
hw/virtio/Kconfig | 11 +
hw/virtio/Makefile.objs | 2 +
hw/virtio/trace-events | 10 +
hw/virtio/virtio-balloon.c | 8 +-
hw/virtio/virtio-mem-pci.c | 157 ++++
hw/virtio/virtio-mem-pci.h | 34 +
hw/virtio/virtio-mem.c | 872 ++++++++++++++++++++
include/exec/memory.h | 41 +
include/hw/boards.h | 1 +
include/hw/pci/pci.h | 1 +
include/hw/vfio/vfio-common.h | 4 +-
include/hw/virtio/virtio-mem.h | 86 ++
include/migration/colo.h | 2 +-
include/migration/misc.h | 2 +
include/standard-headers/linux/virtio_ids.h | 1 +
include/standard-headers/linux/virtio_mem.h | 211 +++++
include/sysemu/balloon.h | 2 -
migration/migration.c | 15 +-
migration/postcopy-ram.c | 23 -
migration/rdma.c | 18 +-
migration/savevm.c | 11 +-
monitor/hmp-cmds.c | 16 +
monitor/monitor.c | 1 +
qapi/misc.json | 64 +-
target/i386/sev.c | 7 +
41 files changed, 1730 insertions(+), 126 deletions(-)
create mode 100644 hw/virtio/virtio-mem-pci.c
create mode 100644 hw/virtio/virtio-mem-pci.h
create mode 100644 hw/virtio/virtio-mem.c
create mode 100644 include/hw/virtio/virtio-mem.h
create mode 100644 include/standard-headers/linux/virtio_mem.h
--
2.26.2
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 01/21] exec: Introduce ram_block_discard_(disable|require)()
2020-06-10 11:53 [PATCH v4 00/21] virtio-mem: Paravirtualized memory hot(un)plug David Hildenbrand
@ 2020-06-10 11:53 ` David Hildenbrand
2020-06-10 11:54 ` [PATCH v4 02/21] vfio: Convert to ram_block_discard_disable() David Hildenbrand
` (20 subsequent siblings)
21 siblings, 0 replies; 34+ messages in thread
From: David Hildenbrand @ 2020-06-10 11:53 UTC (permalink / raw)
To: qemu-devel
Cc: kvm, qemu-s390x, Richard Henderson, Paolo Bonzini,
Dr . David Alan Gilbert, Eduardo Habkost, Michael S . Tsirkin,
David Hildenbrand
We want to replace qemu_balloon_inhibit() by something more generic.
Especially, we want to make sure that technologies that really rely on
RAM block discards to work reliably to run mutual exclusive with
technologies that effectively break it.
E.g., vfio will usually pin all guest memory, turning the virtio-balloon
basically useless and make the VM consume more memory than reported via
the balloon. While the balloon is special already (=> no guarantees, same
behavior possible afer reboots and with huge pages), this will be
different, especially, with virtio-mem.
Let's implement a way such that we can make both types of technology run
mutually exclusive. We'll convert existing balloon inhibitors in successive
patches and add some new ones. Add the check to
qemu_balloon_is_inhibited() for now. We might want to make
virtio-balloon an acutal inhibitor in the future - however, that
requires more thought to not break existing setups.
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: Richard Henderson <rth@twiddle.net>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: David Hildenbrand <david@redhat.com>
---
balloon.c | 3 ++-
exec.c | 52 +++++++++++++++++++++++++++++++++++++++++++
include/exec/memory.h | 41 ++++++++++++++++++++++++++++++++++
3 files changed, 95 insertions(+), 1 deletion(-)
diff --git a/balloon.c b/balloon.c
index f104b42961..5fff79523a 100644
--- a/balloon.c
+++ b/balloon.c
@@ -40,7 +40,8 @@ static int balloon_inhibit_count;
bool qemu_balloon_is_inhibited(void)
{
- return atomic_read(&balloon_inhibit_count) > 0;
+ return atomic_read(&balloon_inhibit_count) > 0 ||
+ ram_block_discard_is_disabled();
}
void qemu_balloon_inhibit(bool state)
diff --git a/exec.c b/exec.c
index be4be2df3a..c4c1d9df84 100644
--- a/exec.c
+++ b/exec.c
@@ -4051,4 +4051,56 @@ void mtree_print_dispatch(AddressSpaceDispatch *d, MemoryRegion *root)
}
}
+/*
+ * If positive, discarding RAM is disabled. If negative, discarding RAM is
+ * required to work and cannot be disabled.
+ */
+static int ram_block_discard_disabled;
+
+int ram_block_discard_disable(bool state)
+{
+ int old;
+
+ if (!state) {
+ atomic_dec(&ram_block_discard_disabled);
+ return 0;
+ }
+
+ do {
+ old = atomic_read(&ram_block_discard_disabled);
+ if (old < 0) {
+ return -EBUSY;
+ }
+ } while (atomic_cmpxchg(&ram_block_discard_disabled, old, old + 1) != old);
+ return 0;
+}
+
+int ram_block_discard_require(bool state)
+{
+ int old;
+
+ if (!state) {
+ atomic_inc(&ram_block_discard_disabled);
+ return 0;
+ }
+
+ do {
+ old = atomic_read(&ram_block_discard_disabled);
+ if (old > 0) {
+ return -EBUSY;
+ }
+ } while (atomic_cmpxchg(&ram_block_discard_disabled, old, old - 1) != old);
+ return 0;
+}
+
+bool ram_block_discard_is_disabled(void)
+{
+ return atomic_read(&ram_block_discard_disabled) > 0;
+}
+
+bool ram_block_discard_is_required(void)
+{
+ return atomic_read(&ram_block_discard_disabled) < 0;
+}
+
#endif
diff --git a/include/exec/memory.h b/include/exec/memory.h
index 3e00cdbbfa..eea7d284b9 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -2474,6 +2474,47 @@ static inline MemOp devend_memop(enum device_endian end)
}
#endif
+/*
+ * Inhibit technologies that require discarding of pages in RAM blocks, e.g.,
+ * to manage the actual amount of memory consumed by the VM (then, the memory
+ * provided by RAM blocks might be bigger than the desired memory consumption).
+ * This *must* be set if:
+ * - Discarding parts of a RAM blocks does not result in the change being
+ * reflected in the VM and the pages getting freed.
+ * - All memory in RAM blocks is pinned or duplicated, invaldiating any previous
+ * discards blindly.
+ * - Discarding parts of a RAM blocks will result in integrity issues (e.g.,
+ * encrypted VMs).
+ * Technologies that only temporarily pin the current working set of a
+ * driver are fine, because we don't expect such pages to be discarded
+ * (esp. based on guest action like balloon inflation).
+ *
+ * This is *not* to be used to protect from concurrent discards (esp.,
+ * postcopy).
+ *
+ * Returns 0 if successful. Returns -EBUSY if a technology that relies on
+ * discards to work reliably is active.
+ */
+int ram_block_discard_disable(bool state);
+
+/*
+ * Inhibit technologies that disable discarding of pages in RAM blocks.
+ *
+ * Returns 0 if successful. Returns -EBUSY if discards are already set to
+ * broken.
+ */
+int ram_block_discard_require(bool state);
+
+/*
+ * Test if discarding of memory in ram blocks is disabled.
+ */
+bool ram_block_discard_is_disabled(void);
+
+/*
+ * Test if discarding of memory in ram blocks is required to work reliably.
+ */
+bool ram_block_discard_is_required(void);
+
#endif
#endif
--
2.26.2
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH v4 02/21] vfio: Convert to ram_block_discard_disable()
2020-06-10 11:53 [PATCH v4 00/21] virtio-mem: Paravirtualized memory hot(un)plug David Hildenbrand
2020-06-10 11:53 ` [PATCH v4 01/21] exec: Introduce ram_block_discard_(disable|require)() David Hildenbrand
@ 2020-06-10 11:54 ` David Hildenbrand
2020-06-10 13:04 ` Tony Krowiak
2020-06-16 11:15 ` Cornelia Huck
2020-06-10 11:54 ` [PATCH v4 03/21] accel/kvm: " David Hildenbrand
` (19 subsequent siblings)
21 siblings, 2 replies; 34+ messages in thread
From: David Hildenbrand @ 2020-06-10 11:54 UTC (permalink / raw)
To: qemu-devel
Cc: kvm, qemu-s390x, Richard Henderson, Paolo Bonzini,
Dr . David Alan Gilbert, Eduardo Habkost, Michael S . Tsirkin,
David Hildenbrand, Cornelia Huck, Alex Williamson,
Christian Borntraeger, Tony Krowiak, Halil Pasic, Pierre Morel,
Eric Farman
VFIO is (except devices without a physical IOMMU or some mediated devices)
incompatible with discarding of RAM. The kernel will pin basically all VM
memory. Let's convert to ram_block_discard_disable(), which can now
fail, in contrast to qemu_balloon_inhibit().
Leave "x-balloon-allowed" named as it is for now.
Cc: Cornelia Huck <cohuck@redhat.com>
Cc: Alex Williamson <alex.williamson@redhat.com>
Cc: Christian Borntraeger <borntraeger@de.ibm.com>
Cc: Tony Krowiak <akrowiak@linux.ibm.com>
Cc: Halil Pasic <pasic@linux.ibm.com>
Cc: Pierre Morel <pmorel@linux.ibm.com>
Cc: Eric Farman <farman@linux.ibm.com>
Signed-off-by: David Hildenbrand <david@redhat.com>
---
hw/vfio/ap.c | 10 +++----
hw/vfio/ccw.c | 11 ++++----
hw/vfio/common.c | 53 +++++++++++++++++++----------------
hw/vfio/pci.c | 6 ++--
include/hw/vfio/vfio-common.h | 4 +--
5 files changed, 45 insertions(+), 39 deletions(-)
diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c
index 95564c17ed..d0b1bc7581 100644
--- a/hw/vfio/ap.c
+++ b/hw/vfio/ap.c
@@ -105,12 +105,12 @@ static void vfio_ap_realize(DeviceState *dev, Error **errp)
vapdev->vdev.dev = dev;
/*
- * vfio-ap devices operate in a way compatible with
- * memory ballooning, as no pages are pinned in the host.
- * This needs to be set before vfio_get_device() for vfio common to
- * handle the balloon inhibitor.
+ * vfio-ap devices operate in a way compatible discarding of memory in
+ * RAM blocks, as no pages are pinned in the host. This needs to be
+ * set before vfio_get_device() for vfio common to handle
+ * ram_block_discard_disable().
*/
- vapdev->vdev.balloon_allowed = true;
+ vapdev->vdev.ram_block_discard_allowed = true;
ret = vfio_get_device(vfio_group, mdevid, &vapdev->vdev, errp);
if (ret) {
diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c
index 63406184d2..82857f1615 100644
--- a/hw/vfio/ccw.c
+++ b/hw/vfio/ccw.c
@@ -418,12 +418,13 @@ static void vfio_ccw_get_device(VFIOGroup *group, VFIOCCWDevice *vcdev,
/*
* All vfio-ccw devices are believed to operate in a way compatible with
- * memory ballooning, ie. pages pinned in the host are in the current
- * working set of the guest driver and therefore never overlap with pages
- * available to the guest balloon driver. This needs to be set before
- * vfio_get_device() for vfio common to handle the balloon inhibitor.
+ * discarding of memory in RAM blocks, ie. pages pinned in the host are
+ * in the current working set of the guest driver and therefore never
+ * overlap e.g., with pages available to the guest balloon driver. This
+ * needs to be set before vfio_get_device() for vfio common to handle
+ * ram_block_discard_disable().
*/
- vcdev->vdev.balloon_allowed = true;
+ vcdev->vdev.ram_block_discard_allowed = true;
if (vfio_get_device(group, vcdev->cdev.mdevid, &vcdev->vdev, errp)) {
goto out_err;
diff --git a/hw/vfio/common.c b/hw/vfio/common.c
index 0b3593b3c0..33357140b8 100644
--- a/hw/vfio/common.c
+++ b/hw/vfio/common.c
@@ -33,7 +33,6 @@
#include "qemu/error-report.h"
#include "qemu/main-loop.h"
#include "qemu/range.h"
-#include "sysemu/balloon.h"
#include "sysemu/kvm.h"
#include "sysemu/reset.h"
#include "trace.h"
@@ -1215,31 +1214,36 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as,
space = vfio_get_address_space(as);
/*
- * VFIO is currently incompatible with memory ballooning insofar as the
+ * VFIO is currently incompatible with discarding of RAM insofar as the
* madvise to purge (zap) the page from QEMU's address space does not
* interact with the memory API and therefore leaves stale virtual to
* physical mappings in the IOMMU if the page was previously pinned. We
- * therefore add a balloon inhibit for each group added to a container,
+ * therefore set discarding broken for each group added to a container,
* whether the container is used individually or shared. This provides
* us with options to allow devices within a group to opt-in and allow
- * ballooning, so long as it is done consistently for a group (for instance
+ * discarding, so long as it is done consistently for a group (for instance
* if the device is an mdev device where it is known that the host vendor
* driver will never pin pages outside of the working set of the guest
- * driver, which would thus not be ballooning candidates).
+ * driver, which would thus not be discarding candidates).
*
* The first opportunity to induce pinning occurs here where we attempt to
* attach the group to existing containers within the AddressSpace. If any
- * pages are already zapped from the virtual address space, such as from a
- * previous ballooning opt-in, new pinning will cause valid mappings to be
+ * pages are already zapped from the virtual address space, such as from
+ * previous discards, new pinning will cause valid mappings to be
* re-established. Likewise, when the overall MemoryListener for a new
* container is registered, a replay of mappings within the AddressSpace
* will occur, re-establishing any previously zapped pages as well.
*
- * NB. Balloon inhibiting does not currently block operation of the
- * balloon driver or revoke previously pinned pages, it only prevents
- * calling madvise to modify the virtual mapping of ballooned pages.
+ * Especially virtio-balloon is currently only prevented from discarding
+ * new memory, it will not yet set ram_block_discard_set_required() and
+ * therefore, neither stops us here or deals with the sudden memory
+ * consumption of inflated memory.
*/
- qemu_balloon_inhibit(true);
+ ret = ram_block_discard_disable(true);
+ if (ret) {
+ error_setg_errno(errp, -ret, "Cannot set discarding of RAM broken");
+ return ret;
+ }
QLIST_FOREACH(container, &space->containers, next) {
if (!ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &container->fd)) {
@@ -1405,7 +1409,7 @@ close_fd_exit:
close(fd);
put_space_exit:
- qemu_balloon_inhibit(false);
+ ram_block_discard_disable(false);
vfio_put_address_space(space);
return ret;
@@ -1526,8 +1530,8 @@ void vfio_put_group(VFIOGroup *group)
return;
}
- if (!group->balloon_allowed) {
- qemu_balloon_inhibit(false);
+ if (!group->ram_block_discard_allowed) {
+ ram_block_discard_disable(false);
}
vfio_kvm_device_del_group(group);
vfio_disconnect_container(group);
@@ -1565,22 +1569,23 @@ int vfio_get_device(VFIOGroup *group, const char *name,
}
/*
- * Clear the balloon inhibitor for this group if the driver knows the
- * device operates compatibly with ballooning. Setting must be consistent
- * per group, but since compatibility is really only possible with mdev
- * currently, we expect singleton groups.
+ * Set discarding of RAM as not broken for this group if the driver knows
+ * the device operates compatibly with discarding. Setting must be
+ * consistent per group, but since compatibility is really only possible
+ * with mdev currently, we expect singleton groups.
*/
- if (vbasedev->balloon_allowed != group->balloon_allowed) {
+ if (vbasedev->ram_block_discard_allowed !=
+ group->ram_block_discard_allowed) {
if (!QLIST_EMPTY(&group->device_list)) {
- error_setg(errp,
- "Inconsistent device balloon setting within group");
+ error_setg(errp, "Inconsistent setting of support for discarding "
+ "RAM (e.g., balloon) within group");
close(fd);
return -1;
}
- if (!group->balloon_allowed) {
- group->balloon_allowed = true;
- qemu_balloon_inhibit(false);
+ if (!group->ram_block_discard_allowed) {
+ group->ram_block_discard_allowed = true;
+ ram_block_discard_disable(false);
}
}
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index 342dd6b912..c33c11b7e4 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -2796,7 +2796,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
}
/*
- * Mediated devices *might* operate compatibly with memory ballooning, but
+ * Mediated devices *might* operate compatibly with discarding of RAM, but
* we cannot know for certain, it depends on whether the mdev vendor driver
* stays in sync with the active working set of the guest driver. Prevent
* the x-balloon-allowed option unless this is minimally an mdev device.
@@ -2809,7 +2809,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
trace_vfio_mdev(vdev->vbasedev.name, is_mdev);
- if (vdev->vbasedev.balloon_allowed && !is_mdev) {
+ if (vdev->vbasedev.ram_block_discard_allowed && !is_mdev) {
error_setg(errp, "x-balloon-allowed only potentially compatible "
"with mdev devices");
vfio_put_group(group);
@@ -3163,7 +3163,7 @@ static Property vfio_pci_dev_properties[] = {
VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT, false),
DEFINE_PROP_BOOL("x-no-mmap", VFIOPCIDevice, vbasedev.no_mmap, false),
DEFINE_PROP_BOOL("x-balloon-allowed", VFIOPCIDevice,
- vbasedev.balloon_allowed, false),
+ vbasedev.ram_block_discard_allowed, false),
DEFINE_PROP_BOOL("x-no-kvm-intx", VFIOPCIDevice, no_kvm_intx, false),
DEFINE_PROP_BOOL("x-no-kvm-msi", VFIOPCIDevice, no_kvm_msi, false),
DEFINE_PROP_BOOL("x-no-kvm-msix", VFIOPCIDevice, no_kvm_msix, false),
diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h
index fd564209ac..c78f3ff559 100644
--- a/include/hw/vfio/vfio-common.h
+++ b/include/hw/vfio/vfio-common.h
@@ -108,7 +108,7 @@ typedef struct VFIODevice {
bool reset_works;
bool needs_reset;
bool no_mmap;
- bool balloon_allowed;
+ bool ram_block_discard_allowed;
VFIODeviceOps *ops;
unsigned int num_irqs;
unsigned int num_regions;
@@ -128,7 +128,7 @@ typedef struct VFIOGroup {
QLIST_HEAD(, VFIODevice) device_list;
QLIST_ENTRY(VFIOGroup) next;
QLIST_ENTRY(VFIOGroup) container_next;
- bool balloon_allowed;
+ bool ram_block_discard_allowed;
} VFIOGroup;
typedef struct VFIODMABuf {
--
2.26.2
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH v4 03/21] accel/kvm: Convert to ram_block_discard_disable()
2020-06-10 11:53 [PATCH v4 00/21] virtio-mem: Paravirtualized memory hot(un)plug David Hildenbrand
2020-06-10 11:53 ` [PATCH v4 01/21] exec: Introduce ram_block_discard_(disable|require)() David Hildenbrand
2020-06-10 11:54 ` [PATCH v4 02/21] vfio: Convert to ram_block_discard_disable() David Hildenbrand
@ 2020-06-10 11:54 ` David Hildenbrand
2020-06-10 11:54 ` [PATCH v4 04/21] s390x/pv: " David Hildenbrand
` (18 subsequent siblings)
21 siblings, 0 replies; 34+ messages in thread
From: David Hildenbrand @ 2020-06-10 11:54 UTC (permalink / raw)
To: qemu-devel
Cc: kvm, qemu-s390x, Richard Henderson, Paolo Bonzini,
Dr . David Alan Gilbert, Eduardo Habkost, Michael S . Tsirkin,
David Hildenbrand
Discarding memory does not work as expected. At the time this is called,
we cannot have anyone active that relies on discards to work properly.
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: David Hildenbrand <david@redhat.com>
---
accel/kvm/kvm-all.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
index d06cc04079..fa18b2caae 100644
--- a/accel/kvm/kvm-all.c
+++ b/accel/kvm/kvm-all.c
@@ -40,7 +40,6 @@
#include "trace.h"
#include "hw/irq.h"
#include "sysemu/sev.h"
-#include "sysemu/balloon.h"
#include "qapi/visitor.h"
#include "qapi/qapi-types-common.h"
#include "qapi/qapi-visit-common.h"
@@ -2143,7 +2142,8 @@ static int kvm_init(MachineState *ms)
s->sync_mmu = !!kvm_vm_check_extension(kvm_state, KVM_CAP_SYNC_MMU);
if (!s->sync_mmu) {
- qemu_balloon_inhibit(true);
+ ret = ram_block_discard_disable(true);
+ assert(!ret);
}
return 0;
--
2.26.2
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH v4 04/21] s390x/pv: Convert to ram_block_discard_disable()
2020-06-10 11:53 [PATCH v4 00/21] virtio-mem: Paravirtualized memory hot(un)plug David Hildenbrand
` (2 preceding siblings ...)
2020-06-10 11:54 ` [PATCH v4 03/21] accel/kvm: " David Hildenbrand
@ 2020-06-10 11:54 ` David Hildenbrand
2020-06-16 11:17 ` Cornelia Huck
2020-06-10 11:54 ` [PATCH v4 05/21] virtio-balloon: Rip out qemu_balloon_inhibit() David Hildenbrand
` (17 subsequent siblings)
21 siblings, 1 reply; 34+ messages in thread
From: David Hildenbrand @ 2020-06-10 11:54 UTC (permalink / raw)
To: qemu-devel
Cc: kvm, qemu-s390x, Richard Henderson, Paolo Bonzini,
Dr . David Alan Gilbert, Eduardo Habkost, Michael S . Tsirkin,
David Hildenbrand, Cornelia Huck, Halil Pasic,
Christian Borntraeger, Janosch Frank
Discarding RAM does not work as expected with protected VMs. Let's
switch to ram_block_discard_disable() for now, as we want to get rid
of qemu_balloon_inhibit(). Note that it will currently never fail, but
might fail in the future with new technologies (e.g., virtio-mem).
Cc: Richard Henderson <rth@twiddle.net>
Cc: Cornelia Huck <cohuck@redhat.com>
Cc: Halil Pasic <pasic@linux.ibm.com>
Cc: Christian Borntraeger <borntraeger@de.ibm.com>
Cc: Janosch Frank <frankja@linux.ibm.com>
Signed-off-by: David Hildenbrand <david@redhat.com>
---
hw/s390x/s390-virtio-ccw.c | 22 +++++++++++++---------
1 file changed, 13 insertions(+), 9 deletions(-)
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
index 60b16fef77..c985bb56eb 100644
--- a/hw/s390x/s390-virtio-ccw.c
+++ b/hw/s390x/s390-virtio-ccw.c
@@ -43,7 +43,6 @@
#include "hw/qdev-properties.h"
#include "hw/s390x/tod.h"
#include "sysemu/sysemu.h"
-#include "sysemu/balloon.h"
#include "hw/s390x/pv.h"
#include "migration/blocker.h"
@@ -329,7 +328,7 @@ static void s390_machine_unprotect(S390CcwMachineState *ms)
ms->pv = false;
migrate_del_blocker(pv_mig_blocker);
error_free_or_abort(&pv_mig_blocker);
- qemu_balloon_inhibit(false);
+ ram_block_discard_disable(false);
}
static int s390_machine_protect(S390CcwMachineState *ms)
@@ -338,17 +337,22 @@ static int s390_machine_protect(S390CcwMachineState *ms)
int rc;
/*
- * Ballooning on protected VMs needs support in the guest for
- * sharing and unsharing balloon pages. Block ballooning for
- * now, until we have a solution to make at least Linux guests
- * either support it or fail gracefully.
+ * Discarding of memory in RAM blocks does not work as expected with
+ * protected VMs. Sharing and unsharing pages would be required. Disable
+ * it for now, until until we have a solution to make at least Linux
+ * guests either support it (e.g., virtio-balloon) or fail gracefully.
*/
- qemu_balloon_inhibit(true);
+ rc = ram_block_discard_disable(true);
+ if (rc) {
+ error_report("protected VMs: cannot disable RAM discard");
+ return rc;
+ }
+
error_setg(&pv_mig_blocker,
"protected VMs are currently not migrateable.");
rc = migrate_add_blocker(pv_mig_blocker, &local_err);
if (rc) {
- qemu_balloon_inhibit(false);
+ ram_block_discard_disable(false);
error_report_err(local_err);
error_free_or_abort(&pv_mig_blocker);
return rc;
@@ -357,7 +361,7 @@ static int s390_machine_protect(S390CcwMachineState *ms)
/* Create SE VM */
rc = s390_pv_vm_enable();
if (rc) {
- qemu_balloon_inhibit(false);
+ ram_block_discard_disable(false);
migrate_del_blocker(pv_mig_blocker);
error_free_or_abort(&pv_mig_blocker);
return rc;
--
2.26.2
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH v4 05/21] virtio-balloon: Rip out qemu_balloon_inhibit()
2020-06-10 11:53 [PATCH v4 00/21] virtio-mem: Paravirtualized memory hot(un)plug David Hildenbrand
` (3 preceding siblings ...)
2020-06-10 11:54 ` [PATCH v4 04/21] s390x/pv: " David Hildenbrand
@ 2020-06-10 11:54 ` David Hildenbrand
2020-06-16 10:56 ` Dr. David Alan Gilbert
2020-06-24 15:32 ` Michael S. Tsirkin
2020-06-10 11:54 ` [PATCH v4 06/21] target/i386: sev: Use ram_block_discard_disable() David Hildenbrand
` (16 subsequent siblings)
21 siblings, 2 replies; 34+ messages in thread
From: David Hildenbrand @ 2020-06-10 11:54 UTC (permalink / raw)
To: qemu-devel
Cc: kvm, qemu-s390x, Richard Henderson, Paolo Bonzini,
Dr . David Alan Gilbert, Eduardo Habkost, Michael S . Tsirkin,
David Hildenbrand, Juan Quintela
The only remaining special case is postcopy. It cannot handle
concurrent discards yet, which would result in requesting already sent
pages from the source. Special-case it in virtio-balloon instead.
Introduce migration_in_incoming_postcopy(), to find out if incoming
postcopy is active.
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: Juan Quintela <quintela@redhat.com>
Cc: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
Signed-off-by: David Hildenbrand <david@redhat.com>
---
balloon.c | 18 ------------------
hw/virtio/virtio-balloon.c | 8 +++++++-
include/migration/misc.h | 2 ++
include/sysemu/balloon.h | 2 --
migration/migration.c | 7 +++++++
migration/postcopy-ram.c | 23 -----------------------
6 files changed, 16 insertions(+), 44 deletions(-)
diff --git a/balloon.c b/balloon.c
index 5fff79523a..354408c6ea 100644
--- a/balloon.c
+++ b/balloon.c
@@ -36,24 +36,6 @@
static QEMUBalloonEvent *balloon_event_fn;
static QEMUBalloonStatus *balloon_stat_fn;
static void *balloon_opaque;
-static int balloon_inhibit_count;
-
-bool qemu_balloon_is_inhibited(void)
-{
- return atomic_read(&balloon_inhibit_count) > 0 ||
- ram_block_discard_is_disabled();
-}
-
-void qemu_balloon_inhibit(bool state)
-{
- if (state) {
- atomic_inc(&balloon_inhibit_count);
- } else {
- atomic_dec(&balloon_inhibit_count);
- }
-
- assert(atomic_read(&balloon_inhibit_count) >= 0);
-}
static bool have_balloon(Error **errp)
{
diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
index 065cd450f1..5ce2f956df 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
@@ -63,6 +63,12 @@ static bool virtio_balloon_pbp_matches(PartiallyBalloonedPage *pbp,
return pbp->base_gpa == base_gpa;
}
+static bool virtio_balloon_inhibited(void)
+{
+ /* Postcopy cannot deal with concurrent discards, so it's special. */
+ return ram_block_discard_is_disabled() || migration_in_incoming_postcopy();
+}
+
static void balloon_inflate_page(VirtIOBalloon *balloon,
MemoryRegion *mr, hwaddr mr_offset,
PartiallyBalloonedPage *pbp)
@@ -360,7 +366,7 @@ static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq)
trace_virtio_balloon_handle_output(memory_region_name(section.mr),
pa);
- if (!qemu_balloon_is_inhibited()) {
+ if (!virtio_balloon_inhibited()) {
if (vq == s->ivq) {
balloon_inflate_page(s, section.mr,
section.offset_within_region, &pbp);
diff --git a/include/migration/misc.h b/include/migration/misc.h
index d2762257aa..34e7d75713 100644
--- a/include/migration/misc.h
+++ b/include/migration/misc.h
@@ -69,6 +69,8 @@ bool migration_has_failed(MigrationState *);
/* ...and after the device transmission */
bool migration_in_postcopy_after_devices(MigrationState *);
void migration_global_dump(Monitor *mon);
+/* True if incomming migration entered POSTCOPY_INCOMING_DISCARD */
+bool migration_in_incoming_postcopy(void);
/* migration/block-dirty-bitmap.c */
void dirty_bitmap_mig_init(void);
diff --git a/include/sysemu/balloon.h b/include/sysemu/balloon.h
index aea0c44985..20a2defe3a 100644
--- a/include/sysemu/balloon.h
+++ b/include/sysemu/balloon.h
@@ -23,7 +23,5 @@ typedef void (QEMUBalloonStatus)(void *opaque, BalloonInfo *info);
int qemu_add_balloon_handler(QEMUBalloonEvent *event_func,
QEMUBalloonStatus *stat_func, void *opaque);
void qemu_remove_balloon_handler(void *opaque);
-bool qemu_balloon_is_inhibited(void);
-void qemu_balloon_inhibit(bool state);
#endif
diff --git a/migration/migration.c b/migration/migration.c
index b63ad91d34..14856cc930 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -1772,6 +1772,13 @@ bool migration_in_postcopy_after_devices(MigrationState *s)
return migration_in_postcopy() && s->postcopy_after_devices;
}
+bool migration_in_incoming_postcopy(void)
+{
+ PostcopyState ps = postcopy_state_get();
+
+ return ps >= POSTCOPY_INCOMING_DISCARD && ps < POSTCOPY_INCOMING_END;
+}
+
bool migration_is_idle(void)
{
MigrationState *s = current_migration;
diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c
index a36402722b..b41a9fe2fd 100644
--- a/migration/postcopy-ram.c
+++ b/migration/postcopy-ram.c
@@ -27,7 +27,6 @@
#include "qemu/notify.h"
#include "qemu/rcu.h"
#include "sysemu/sysemu.h"
-#include "sysemu/balloon.h"
#include "qemu/error-report.h"
#include "trace.h"
#include "hw/boards.h"
@@ -520,20 +519,6 @@ int postcopy_ram_incoming_init(MigrationIncomingState *mis)
return 0;
}
-/*
- * Manage a single vote to the QEMU balloon inhibitor for all postcopy usage,
- * last caller wins.
- */
-static void postcopy_balloon_inhibit(bool state)
-{
- static bool cur_state = false;
-
- if (state != cur_state) {
- qemu_balloon_inhibit(state);
- cur_state = state;
- }
-}
-
/*
* At the end of a migration where postcopy_ram_incoming_init was called.
*/
@@ -565,8 +550,6 @@ int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis)
mis->have_fault_thread = false;
}
- postcopy_balloon_inhibit(false);
-
if (enable_mlock) {
if (os_mlock() < 0) {
error_report("mlock: %s", strerror(errno));
@@ -1160,12 +1143,6 @@ int postcopy_ram_incoming_setup(MigrationIncomingState *mis)
}
memset(mis->postcopy_tmp_zero_page, '\0', mis->largest_page_size);
- /*
- * Ballooning can mark pages as absent while we're postcopying
- * that would cause false userfaults.
- */
- postcopy_balloon_inhibit(true);
-
trace_postcopy_ram_enable_notify();
return 0;
--
2.26.2
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH v4 06/21] target/i386: sev: Use ram_block_discard_disable()
2020-06-10 11:53 [PATCH v4 00/21] virtio-mem: Paravirtualized memory hot(un)plug David Hildenbrand
` (4 preceding siblings ...)
2020-06-10 11:54 ` [PATCH v4 05/21] virtio-balloon: Rip out qemu_balloon_inhibit() David Hildenbrand
@ 2020-06-10 11:54 ` David Hildenbrand
2020-06-10 11:54 ` [PATCH v4 07/21] migration/rdma: " David Hildenbrand
` (15 subsequent siblings)
21 siblings, 0 replies; 34+ messages in thread
From: David Hildenbrand @ 2020-06-10 11:54 UTC (permalink / raw)
To: qemu-devel
Cc: kvm, qemu-s390x, Richard Henderson, Paolo Bonzini,
Dr . David Alan Gilbert, Eduardo Habkost, Michael S . Tsirkin,
David Hildenbrand
AMD SEV will pin all guest memory, mark discarding of RAM broken. At the
time this is called, we cannot have anyone active that relies on discards
to work properly - let's still implement error handling.
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Richard Henderson <rth@twiddle.net>
Cc: Eduardo Habkost <ehabkost@redhat.com>
Signed-off-by: David Hildenbrand <david@redhat.com>
---
target/i386/sev.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/target/i386/sev.c b/target/i386/sev.c
index 51cdbe5496..4a4863db28 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -649,6 +649,12 @@ sev_guest_init(const char *id)
uint32_t host_cbitpos;
struct sev_user_data_status status = {};
+ ret = ram_block_discard_disable(true);
+ if (ret) {
+ error_report("%s: cannot disable RAM discard", __func__);
+ return NULL;
+ }
+
sev_state = s = g_new0(SEVState, 1);
s->sev_info = lookup_sev_guest_info(id);
if (!s->sev_info) {
@@ -724,6 +730,7 @@ sev_guest_init(const char *id)
err:
g_free(sev_state);
sev_state = NULL;
+ ram_block_discard_disable(false);
return NULL;
}
--
2.26.2
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH v4 07/21] migration/rdma: Use ram_block_discard_disable()
2020-06-10 11:53 [PATCH v4 00/21] virtio-mem: Paravirtualized memory hot(un)plug David Hildenbrand
` (5 preceding siblings ...)
2020-06-10 11:54 ` [PATCH v4 06/21] target/i386: sev: Use ram_block_discard_disable() David Hildenbrand
@ 2020-06-10 11:54 ` David Hildenbrand
2020-06-10 11:54 ` [PATCH v4 08/21] migration/colo: " David Hildenbrand
` (14 subsequent siblings)
21 siblings, 0 replies; 34+ messages in thread
From: David Hildenbrand @ 2020-06-10 11:54 UTC (permalink / raw)
To: qemu-devel
Cc: kvm, qemu-s390x, Richard Henderson, Paolo Bonzini,
Dr . David Alan Gilbert, Eduardo Habkost, Michael S . Tsirkin,
David Hildenbrand, Juan Quintela
RDMA will pin all guest memory (as documented in docs/rdma.txt). We want
to disable RAM block discards - however, to keep it simple use
ram_block_discard_is_required() instead of inhibiting.
Note: It is not sufficient to limit disabling to pin_all. Even when only
conditionally pinning 1 MB chunks, as soon as one page within such a
chunk was discarded and one page not, the discarded pages will be pinned
as well.
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: Juan Quintela <quintela@redhat.com>
Cc: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
Signed-off-by: David Hildenbrand <david@redhat.com>
---
migration/rdma.c | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/migration/rdma.c b/migration/rdma.c
index ec45d33ba3..bbe6f36627 100644
--- a/migration/rdma.c
+++ b/migration/rdma.c
@@ -29,6 +29,7 @@
#include "qemu/sockets.h"
#include "qemu/bitmap.h"
#include "qemu/coroutine.h"
+#include "exec/memory.h"
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
@@ -4017,8 +4018,14 @@ void rdma_start_incoming_migration(const char *host_port, Error **errp)
Error *local_err = NULL;
trace_rdma_start_incoming_migration();
- rdma = qemu_rdma_data_init(host_port, &local_err);
+ /* Avoid ram_block_discard_disable(), cannot change during migration. */
+ if (ram_block_discard_is_required()) {
+ error_setg(errp, "RDMA: cannot disable RAM discard");
+ return;
+ }
+
+ rdma = qemu_rdma_data_init(host_port, &local_err);
if (rdma == NULL) {
goto err;
}
@@ -4067,10 +4074,17 @@ void rdma_start_outgoing_migration(void *opaque,
const char *host_port, Error **errp)
{
MigrationState *s = opaque;
- RDMAContext *rdma = qemu_rdma_data_init(host_port, errp);
RDMAContext *rdma_return_path = NULL;
+ RDMAContext *rdma;
int ret = 0;
+ /* Avoid ram_block_discard_disable(), cannot change during migration. */
+ if (ram_block_discard_is_required()) {
+ error_setg(errp, "RDMA: cannot disable RAM discard");
+ return;
+ }
+
+ rdma = qemu_rdma_data_init(host_port, errp);
if (rdma == NULL) {
goto err;
}
--
2.26.2
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH v4 08/21] migration/colo: Use ram_block_discard_disable()
2020-06-10 11:53 [PATCH v4 00/21] virtio-mem: Paravirtualized memory hot(un)plug David Hildenbrand
` (6 preceding siblings ...)
2020-06-10 11:54 ` [PATCH v4 07/21] migration/rdma: " David Hildenbrand
@ 2020-06-10 11:54 ` David Hildenbrand
2020-06-10 11:54 ` [PATCH v4 09/21] linux-headers: update to contain virtio-mem David Hildenbrand
` (13 subsequent siblings)
21 siblings, 0 replies; 34+ messages in thread
From: David Hildenbrand @ 2020-06-10 11:54 UTC (permalink / raw)
To: qemu-devel
Cc: kvm, qemu-s390x, Richard Henderson, Paolo Bonzini,
Dr . David Alan Gilbert, Eduardo Habkost, Michael S . Tsirkin,
David Hildenbrand, Lukas Straub, Hailiang Zhang, Juan Quintela
COLO will copy all memory in a RAM block, disable discarding of RAM.
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Tested-by: Lukas Straub <lukasstraub2@web.de>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: Hailiang Zhang <zhang.zhanghailiang@huawei.com>
Cc: Juan Quintela <quintela@redhat.com>
Cc: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
Signed-off-by: David Hildenbrand <david@redhat.com>
---
include/migration/colo.h | 2 +-
migration/migration.c | 8 +++++++-
migration/savevm.c | 11 +++++++++--
3 files changed, 17 insertions(+), 4 deletions(-)
diff --git a/include/migration/colo.h b/include/migration/colo.h
index 1636e6f907..768e1f04c3 100644
--- a/include/migration/colo.h
+++ b/include/migration/colo.h
@@ -25,7 +25,7 @@ void migrate_start_colo_process(MigrationState *s);
bool migration_in_colo_state(void);
/* loadvm */
-void migration_incoming_enable_colo(void);
+int migration_incoming_enable_colo(void);
void migration_incoming_disable_colo(void);
bool migration_incoming_colo_enabled(void);
void *colo_process_incoming_thread(void *opaque);
diff --git a/migration/migration.c b/migration/migration.c
index 14856cc930..0f6799f5d2 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -338,12 +338,18 @@ bool migration_incoming_colo_enabled(void)
void migration_incoming_disable_colo(void)
{
+ ram_block_discard_disable(false);
migration_colo_enabled = false;
}
-void migration_incoming_enable_colo(void)
+int migration_incoming_enable_colo(void)
{
+ if (ram_block_discard_disable(true)) {
+ error_report("COLO: cannot disable RAM discard");
+ return -EBUSY;
+ }
migration_colo_enabled = true;
+ return 0;
}
void migrate_add_address(SocketAddress *address)
diff --git a/migration/savevm.c b/migration/savevm.c
index c00a6807d9..19b4f9600d 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -2111,8 +2111,15 @@ static int loadvm_handle_recv_bitmap(MigrationIncomingState *mis,
static int loadvm_process_enable_colo(MigrationIncomingState *mis)
{
- migration_incoming_enable_colo();
- return colo_init_ram_cache();
+ int ret = migration_incoming_enable_colo();
+
+ if (!ret) {
+ ret = colo_init_ram_cache();
+ if (ret) {
+ migration_incoming_disable_colo();
+ }
+ }
+ return ret;
}
/*
--
2.26.2
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH v4 09/21] linux-headers: update to contain virtio-mem
2020-06-10 11:53 [PATCH v4 00/21] virtio-mem: Paravirtualized memory hot(un)plug David Hildenbrand
` (7 preceding siblings ...)
2020-06-10 11:54 ` [PATCH v4 08/21] migration/colo: " David Hildenbrand
@ 2020-06-10 11:54 ` David Hildenbrand
2020-06-10 11:54 ` [PATCH v4 10/21] virtio-mem: Paravirtualized memory hot(un)plug David Hildenbrand
` (12 subsequent siblings)
21 siblings, 0 replies; 34+ messages in thread
From: David Hildenbrand @ 2020-06-10 11:54 UTC (permalink / raw)
To: qemu-devel
Cc: kvm, qemu-s390x, Richard Henderson, Paolo Bonzini,
Dr . David Alan Gilbert, Eduardo Habkost, Michael S . Tsirkin,
David Hildenbrand
To be merged hopefully soon. Then, we can replace this by a proper
header sync.
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Signed-off-by: David Hildenbrand <david@redhat.com>
---
include/standard-headers/linux/virtio_ids.h | 1 +
include/standard-headers/linux/virtio_mem.h | 211 ++++++++++++++++++++
2 files changed, 212 insertions(+)
create mode 100644 include/standard-headers/linux/virtio_mem.h
diff --git a/include/standard-headers/linux/virtio_ids.h b/include/standard-headers/linux/virtio_ids.h
index ecc27a1740..b052355ac7 100644
--- a/include/standard-headers/linux/virtio_ids.h
+++ b/include/standard-headers/linux/virtio_ids.h
@@ -44,6 +44,7 @@
#define VIRTIO_ID_VSOCK 19 /* virtio vsock transport */
#define VIRTIO_ID_CRYPTO 20 /* virtio crypto */
#define VIRTIO_ID_IOMMU 23 /* virtio IOMMU */
+#define VIRTIO_ID_MEM 24 /* virtio mem */
#define VIRTIO_ID_FS 26 /* virtio filesystem */
#define VIRTIO_ID_PMEM 27 /* virtio pmem */
#define VIRTIO_ID_MAC80211_HWSIM 29 /* virtio mac80211-hwsim */
diff --git a/include/standard-headers/linux/virtio_mem.h b/include/standard-headers/linux/virtio_mem.h
new file mode 100644
index 0000000000..05e5ade75d
--- /dev/null
+++ b/include/standard-headers/linux/virtio_mem.h
@@ -0,0 +1,211 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Virtio Mem Device
+ *
+ * Copyright Red Hat, Inc. 2020
+ *
+ * Authors:
+ * David Hildenbrand <david@redhat.com>
+ *
+ * This header is BSD licensed so anyone can use the definitions
+ * to implement compatible drivers/servers:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of IBM nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LINUX_VIRTIO_MEM_H
+#define _LINUX_VIRTIO_MEM_H
+
+#include "standard-headers/linux/types.h"
+#include "standard-headers/linux/virtio_types.h"
+#include "standard-headers/linux/virtio_ids.h"
+#include "standard-headers/linux/virtio_config.h"
+
+/*
+ * Each virtio-mem device manages a dedicated region in physical address
+ * space. Each device can belong to a single NUMA node, multiple devices
+ * for a single NUMA node are possible. A virtio-mem device is like a
+ * "resizable DIMM" consisting of small memory blocks that can be plugged
+ * or unplugged. The device driver is responsible for (un)plugging memory
+ * blocks on demand.
+ *
+ * Virtio-mem devices can only operate on their assigned memory region in
+ * order to (un)plug memory. A device cannot (un)plug memory belonging to
+ * other devices.
+ *
+ * The "region_size" corresponds to the maximum amount of memory that can
+ * be provided by a device. The "size" corresponds to the amount of memory
+ * that is currently plugged. "requested_size" corresponds to a request
+ * from the device to the device driver to (un)plug blocks. The
+ * device driver should try to (un)plug blocks in order to reach the
+ * "requested_size". It is impossible to plug more memory than requested.
+ *
+ * The "usable_region_size" represents the memory region that can actually
+ * be used to (un)plug memory. It is always at least as big as the
+ * "requested_size" and will grow dynamically. It will only shrink when
+ * explicitly triggered (VIRTIO_MEM_REQ_UNPLUG).
+ *
+ * There are no guarantees what will happen if unplugged memory is
+ * read/written. Such memory should, in general, not be touched. E.g.,
+ * even writing might succeed, but the values will simply be discarded at
+ * random points in time.
+ *
+ * It can happen that the device cannot process a request, because it is
+ * busy. The device driver has to retry later.
+ *
+ * Usually, during system resets all memory will get unplugged, so the
+ * device driver can start with a clean state. However, in specific
+ * scenarios (if the device is busy) it can happen that the device still
+ * has memory plugged. The device driver can request to unplug all memory
+ * (VIRTIO_MEM_REQ_UNPLUG) - which might take a while to succeed if the
+ * device is busy.
+ */
+
+/* --- virtio-mem: feature bits --- */
+
+/* node_id is an ACPI PXM and is valid */
+#define VIRTIO_MEM_F_ACPI_PXM 0
+
+
+/* --- virtio-mem: guest -> host requests --- */
+
+/* request to plug memory blocks */
+#define VIRTIO_MEM_REQ_PLUG 0
+/* request to unplug memory blocks */
+#define VIRTIO_MEM_REQ_UNPLUG 1
+/* request to unplug all blocks and shrink the usable size */
+#define VIRTIO_MEM_REQ_UNPLUG_ALL 2
+/* request information about the plugged state of memory blocks */
+#define VIRTIO_MEM_REQ_STATE 3
+
+struct virtio_mem_req_plug {
+ __virtio64 addr;
+ __virtio16 nb_blocks;
+ __virtio16 padding[3];
+};
+
+struct virtio_mem_req_unplug {
+ __virtio64 addr;
+ __virtio16 nb_blocks;
+ __virtio16 padding[3];
+};
+
+struct virtio_mem_req_state {
+ __virtio64 addr;
+ __virtio16 nb_blocks;
+ __virtio16 padding[3];
+};
+
+struct virtio_mem_req {
+ __virtio16 type;
+ __virtio16 padding[3];
+
+ union {
+ struct virtio_mem_req_plug plug;
+ struct virtio_mem_req_unplug unplug;
+ struct virtio_mem_req_state state;
+ } u;
+};
+
+
+/* --- virtio-mem: host -> guest response --- */
+
+/*
+ * Request processed successfully, applicable for
+ * - VIRTIO_MEM_REQ_PLUG
+ * - VIRTIO_MEM_REQ_UNPLUG
+ * - VIRTIO_MEM_REQ_UNPLUG_ALL
+ * - VIRTIO_MEM_REQ_STATE
+ */
+#define VIRTIO_MEM_RESP_ACK 0
+/*
+ * Request denied - e.g. trying to plug more than requested, applicable for
+ * - VIRTIO_MEM_REQ_PLUG
+ */
+#define VIRTIO_MEM_RESP_NACK 1
+/*
+ * Request cannot be processed right now, try again later, applicable for
+ * - VIRTIO_MEM_REQ_PLUG
+ * - VIRTIO_MEM_REQ_UNPLUG
+ * - VIRTIO_MEM_REQ_UNPLUG_ALL
+ */
+#define VIRTIO_MEM_RESP_BUSY 2
+/*
+ * Error in request (e.g. addresses/alignment), applicable for
+ * - VIRTIO_MEM_REQ_PLUG
+ * - VIRTIO_MEM_REQ_UNPLUG
+ * - VIRTIO_MEM_REQ_STATE
+ */
+#define VIRTIO_MEM_RESP_ERROR 3
+
+
+/* State of memory blocks is "plugged" */
+#define VIRTIO_MEM_STATE_PLUGGED 0
+/* State of memory blocks is "unplugged" */
+#define VIRTIO_MEM_STATE_UNPLUGGED 1
+/* State of memory blocks is "mixed" */
+#define VIRTIO_MEM_STATE_MIXED 2
+
+struct virtio_mem_resp_state {
+ __virtio16 state;
+};
+
+struct virtio_mem_resp {
+ __virtio16 type;
+ __virtio16 padding[3];
+
+ union {
+ struct virtio_mem_resp_state state;
+ } u;
+};
+
+/* --- virtio-mem: configuration --- */
+
+struct virtio_mem_config {
+ /* Block size and alignment. Cannot change. */
+ uint64_t block_size;
+ /* Valid with VIRTIO_MEM_F_ACPI_PXM. Cannot change. */
+ uint16_t node_id;
+ uint8_t padding[6];
+ /* Start address of the memory region. Cannot change. */
+ uint64_t addr;
+ /* Region size (maximum). Cannot change. */
+ uint64_t region_size;
+ /*
+ * Currently usable region size. Can grow up to region_size. Can
+ * shrink due to VIRTIO_MEM_REQ_UNPLUG_ALL (in which case no config
+ * update will be sent).
+ */
+ uint64_t usable_region_size;
+ /*
+ * Currently used size. Changes due to plug/unplug requests, but no
+ * config updates will be sent.
+ */
+ uint64_t plugged_size;
+ /* Requested size. New plug requests cannot exceed it. Can change. */
+ uint64_t requested_size;
+};
+
+#endif /* _LINUX_VIRTIO_MEM_H */
--
2.26.2
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH v4 10/21] virtio-mem: Paravirtualized memory hot(un)plug
2020-06-10 11:53 [PATCH v4 00/21] virtio-mem: Paravirtualized memory hot(un)plug David Hildenbrand
` (8 preceding siblings ...)
2020-06-10 11:54 ` [PATCH v4 09/21] linux-headers: update to contain virtio-mem David Hildenbrand
@ 2020-06-10 11:54 ` David Hildenbrand
2020-06-10 11:54 ` [PATCH v4 11/21] virtio-pci: Proxy for virtio-mem David Hildenbrand
` (11 subsequent siblings)
21 siblings, 0 replies; 34+ messages in thread
From: David Hildenbrand @ 2020-06-10 11:54 UTC (permalink / raw)
To: qemu-devel
Cc: kvm, qemu-s390x, Richard Henderson, Paolo Bonzini,
Dr . David Alan Gilbert, Eduardo Habkost, Michael S . Tsirkin,
David Hildenbrand, Eric Blake, Markus Armbruster, Igor Mammedov
This is the very basic/initial version of virtio-mem. An introduction to
virtio-mem can be found in the Linux kernel driver [1]. While it can be
used in the current state for hotplug of a smaller amount of memory, it
will heavily benefit from resizeable memory regions in the future.
Each virtio-mem device manages a memory region (provided via a memory
backend). After requested by the hypervisor ("requested-size"), the
guest can try to plug/unplug blocks of memory within that region, in order
to reach the requested size. Initially, and after a reboot, all memory is
unplugged (except in special cases - reboot during postcopy).
The guest may only try to plug/unplug blocks of memory within the usable
region size. The usable region size is a little bigger than the
requested size, to give the device driver some flexibility. The usable
region size will only grow, except on reboots or when all memory is
requested to get unplugged. The guest can never plug more memory than
requested. Unplugged memory will get zapped/discarded, similar to in a
balloon device.
The block size is variable, however, it is always chosen in a way such that
THP splits are avoided (e.g., 2MB). The state of each block
(plugged/unplugged) is tracked in a bitmap.
As virtio-mem devices (e.g., virtio-mem-pci) will be memory devices, we now
expose "VirtioMEMDeviceInfo" via "query-memory-devices".
--------------------------------------------------------------------------
There are two important follow-up items that are in the works:
1. Resizeable memory regions: Use resizeable allocations/RAM blocks to
grow/shrink along with the usable region size. This avoids creating
initially very big VMAs, RAM blocks, and KVM slots.
2. Protection of unplugged memory: Make sure the gust cannot actually
make use of unplugged memory.
Other follow-up items that are in the works:
1. Exclude unplugged memory during migration (via precopy notifier).
2. Handle remapping of memory.
3. Support for other architectures.
--------------------------------------------------------------------------
Example usage (virtio-mem-pci is introduced in follow-up patches):
Start QEMU with two virtio-mem devices (one per NUMA node):
$ qemu-system-x86_64 -m 4G,maxmem=20G \
-smp sockets=2,cores=2 \
-numa node,nodeid=0,cpus=0-1 -numa node,nodeid=1,cpus=2-3 \
[...]
-object memory-backend-ram,id=mem0,size=8G \
-device virtio-mem-pci,id=vm0,memdev=mem0,node=0,requested-size=0M \
-object memory-backend-ram,id=mem1,size=8G \
-device virtio-mem-pci,id=vm1,memdev=mem1,node=1,requested-size=1G
Query the configuration:
(qemu) info memory-devices
Memory device [virtio-mem]: "vm0"
memaddr: 0x140000000
node: 0
requested-size: 0
size: 0
max-size: 8589934592
block-size: 2097152
memdev: /objects/mem0
Memory device [virtio-mem]: "vm1"
memaddr: 0x340000000
node: 1
requested-size: 1073741824
size: 1073741824
max-size: 8589934592
block-size: 2097152
memdev: /objects/mem1
Add some memory to node 0:
(qemu) qom-set vm0 requested-size 500M
Remove some memory from node 1:
(qemu) qom-set vm1 requested-size 200M
Query the configuration again:
(qemu) info memory-devices
Memory device [virtio-mem]: "vm0"
memaddr: 0x140000000
node: 0
requested-size: 524288000
size: 524288000
max-size: 8589934592
block-size: 2097152
memdev: /objects/mem0
Memory device [virtio-mem]: "vm1"
memaddr: 0x340000000
node: 1
requested-size: 209715200
size: 209715200
max-size: 8589934592
block-size: 2097152
memdev: /objects/mem1
[1] https://lkml.kernel.org/r/20200311171422.10484-1-david@redhat.com
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: Eric Blake <eblake@redhat.com>
Cc: Markus Armbruster <armbru@redhat.com>
Cc: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
Cc: Igor Mammedov <imammedo@redhat.com>
Signed-off-by: David Hildenbrand <david@redhat.com>
---
hw/virtio/Kconfig | 11 +
hw/virtio/Makefile.objs | 1 +
hw/virtio/virtio-mem.c | 724 +++++++++++++++++++++++++++++++++
include/hw/virtio/virtio-mem.h | 78 ++++
qapi/misc.json | 39 +-
5 files changed, 852 insertions(+), 1 deletion(-)
create mode 100644 hw/virtio/virtio-mem.c
create mode 100644 include/hw/virtio/virtio-mem.h
diff --git a/hw/virtio/Kconfig b/hw/virtio/Kconfig
index 83122424fa..0eda25c4e1 100644
--- a/hw/virtio/Kconfig
+++ b/hw/virtio/Kconfig
@@ -47,3 +47,14 @@ config VIRTIO_PMEM
depends on VIRTIO
depends on VIRTIO_PMEM_SUPPORTED
select MEM_DEVICE
+
+config VIRTIO_MEM_SUPPORTED
+ bool
+
+config VIRTIO_MEM
+ bool
+ default y
+ depends on VIRTIO
+ depends on LINUX
+ depends on VIRTIO_MEM_SUPPORTED
+ select MEM_DEVICE
diff --git a/hw/virtio/Makefile.objs b/hw/virtio/Makefile.objs
index 4e4d39a0a4..7df70e977e 100644
--- a/hw/virtio/Makefile.objs
+++ b/hw/virtio/Makefile.objs
@@ -18,6 +18,7 @@ common-obj-$(call land,$(CONFIG_VIRTIO_PMEM),$(CONFIG_VIRTIO_PCI)) += virtio-pme
obj-$(call land,$(CONFIG_VHOST_USER_FS),$(CONFIG_VIRTIO_PCI)) += vhost-user-fs-pci.o
obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iommu.o
obj-$(CONFIG_VHOST_VSOCK) += vhost-vsock.o
+obj-$(CONFIG_VIRTIO_MEM) += virtio-mem.o
ifeq ($(CONFIG_VIRTIO_PCI),y)
obj-$(CONFIG_VHOST_VSOCK) += vhost-vsock-pci.o
diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c
new file mode 100644
index 0000000000..d8a0c974d3
--- /dev/null
+++ b/hw/virtio/virtio-mem.c
@@ -0,0 +1,724 @@
+/*
+ * Virtio MEM device
+ *
+ * Copyright (C) 2020 Red Hat, Inc.
+ *
+ * Authors:
+ * David Hildenbrand <david@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/iov.h"
+#include "qemu/cutils.h"
+#include "qemu/error-report.h"
+#include "qemu/units.h"
+#include "sysemu/numa.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/reset.h"
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/virtio-bus.h"
+#include "hw/virtio/virtio-access.h"
+#include "hw/virtio/virtio-mem.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "exec/ram_addr.h"
+#include "migration/misc.h"
+#include "hw/boards.h"
+#include "hw/qdev-properties.h"
+#include "config-devices.h"
+
+/*
+ * Use QEMU_VMALLOC_ALIGN, so no THP will have to be split when unplugging
+ * memory (e.g., 2MB on x86_64).
+ */
+#define VIRTIO_MEM_MIN_BLOCK_SIZE QEMU_VMALLOC_ALIGN
+/*
+ * Size the usable region bigger than the requested size if possible. Esp.
+ * Linux guests will only add (aligned) memory blocks in case they fully
+ * fit into the usable region, but plug+online only a subset of the pages.
+ * The memory block size corresponds mostly to the section size.
+ *
+ * This allows e.g., to add 20MB with a section size of 128MB on x86_64, and
+ * a section size of 1GB on arm64 (as long as the start address is properly
+ * aligned, similar to ordinary DIMMs).
+ *
+ * We can change this at any time and maybe even make it configurable if
+ * necessary (as the section size can change). But it's more likely that the
+ * section size will rather get smaller and not bigger over time.
+ */
+#if defined(__x86_64__)
+#define VIRTIO_MEM_USABLE_EXTENT (2 * (128 * MiB))
+#else
+#error VIRTIO_MEM_USABLE_EXTENT not defined
+#endif
+
+static bool virtio_mem_is_busy(void)
+{
+ /*
+ * Postcopy cannot handle concurrent discards and we don't want to migrate
+ * pages on-demand with stale content when plugging new blocks.
+ */
+ return migration_in_incoming_postcopy();
+}
+
+static bool virtio_mem_test_bitmap(VirtIOMEM *vmem, uint64_t start_gpa,
+ uint64_t size, bool plugged)
+{
+ const unsigned long first_bit = (start_gpa - vmem->addr) / vmem->block_size;
+ const unsigned long last_bit = first_bit + (size / vmem->block_size) - 1;
+ unsigned long found_bit;
+
+ /* We fake a shorter bitmap to avoid searching too far. */
+ if (plugged) {
+ found_bit = find_next_zero_bit(vmem->bitmap, last_bit + 1, first_bit);
+ } else {
+ found_bit = find_next_bit(vmem->bitmap, last_bit + 1, first_bit);
+ }
+ return found_bit > last_bit;
+}
+
+static void virtio_mem_set_bitmap(VirtIOMEM *vmem, uint64_t start_gpa,
+ uint64_t size, bool plugged)
+{
+ const unsigned long bit = (start_gpa - vmem->addr) / vmem->block_size;
+ const unsigned long nbits = size / vmem->block_size;
+
+ if (plugged) {
+ bitmap_set(vmem->bitmap, bit, nbits);
+ } else {
+ bitmap_clear(vmem->bitmap, bit, nbits);
+ }
+}
+
+static void virtio_mem_send_response(VirtIOMEM *vmem, VirtQueueElement *elem,
+ struct virtio_mem_resp *resp)
+{
+ VirtIODevice *vdev = VIRTIO_DEVICE(vmem);
+ VirtQueue *vq = vmem->vq;
+
+ iov_from_buf(elem->in_sg, elem->in_num, 0, resp, sizeof(*resp));
+
+ virtqueue_push(vq, elem, sizeof(*resp));
+ virtio_notify(vdev, vq);
+}
+
+static void virtio_mem_send_response_simple(VirtIOMEM *vmem,
+ VirtQueueElement *elem,
+ uint16_t type)
+{
+ struct virtio_mem_resp resp = {
+ .type = cpu_to_le16(type),
+ };
+
+ virtio_mem_send_response(vmem, elem, &resp);
+}
+
+static bool virtio_mem_valid_range(VirtIOMEM *vmem, uint64_t gpa, uint64_t size)
+{
+ if (!QEMU_IS_ALIGNED(gpa, vmem->block_size)) {
+ return false;
+ }
+ if (gpa + size < gpa || !size) {
+ return false;
+ }
+ if (gpa < vmem->addr || gpa >= vmem->addr + vmem->usable_region_size) {
+ return false;
+ }
+ if (gpa + size > vmem->addr + vmem->usable_region_size) {
+ return false;
+ }
+ return true;
+}
+
+static int virtio_mem_set_block_state(VirtIOMEM *vmem, uint64_t start_gpa,
+ uint64_t size, bool plug)
+{
+ const uint64_t offset = start_gpa - vmem->addr;
+ int ret;
+
+ if (virtio_mem_is_busy()) {
+ return -EBUSY;
+ }
+
+ if (!plug) {
+ ret = ram_block_discard_range(vmem->memdev->mr.ram_block, offset, size);
+ if (ret) {
+ error_report("Unexpected error discarding RAM: %s",
+ strerror(-ret));
+ return -EBUSY;
+ }
+ }
+ virtio_mem_set_bitmap(vmem, start_gpa, size, plug);
+ return 0;
+}
+
+static int virtio_mem_state_change_request(VirtIOMEM *vmem, uint64_t gpa,
+ uint16_t nb_blocks, bool plug)
+{
+ const uint64_t size = nb_blocks * vmem->block_size;
+ int ret;
+
+ if (!virtio_mem_valid_range(vmem, gpa, size)) {
+ return VIRTIO_MEM_RESP_ERROR;
+ }
+
+ if (plug && (vmem->size + size > vmem->requested_size)) {
+ return VIRTIO_MEM_RESP_NACK;
+ }
+
+ /* test if really all blocks are in the opposite state */
+ if (!virtio_mem_test_bitmap(vmem, gpa, size, !plug)) {
+ return VIRTIO_MEM_RESP_ERROR;
+ }
+
+ ret = virtio_mem_set_block_state(vmem, gpa, size, plug);
+ if (ret) {
+ return VIRTIO_MEM_RESP_BUSY;
+ }
+ if (plug) {
+ vmem->size += size;
+ } else {
+ vmem->size -= size;
+ }
+ return VIRTIO_MEM_RESP_ACK;
+}
+
+static void virtio_mem_plug_request(VirtIOMEM *vmem, VirtQueueElement *elem,
+ struct virtio_mem_req *req)
+{
+ const uint64_t gpa = le64_to_cpu(req->u.plug.addr);
+ const uint16_t nb_blocks = le16_to_cpu(req->u.plug.nb_blocks);
+ uint16_t type;
+
+ type = virtio_mem_state_change_request(vmem, gpa, nb_blocks, true);
+ virtio_mem_send_response_simple(vmem, elem, type);
+}
+
+static void virtio_mem_unplug_request(VirtIOMEM *vmem, VirtQueueElement *elem,
+ struct virtio_mem_req *req)
+{
+ const uint64_t gpa = le64_to_cpu(req->u.unplug.addr);
+ const uint16_t nb_blocks = le16_to_cpu(req->u.unplug.nb_blocks);
+ uint16_t type;
+
+ type = virtio_mem_state_change_request(vmem, gpa, nb_blocks, false);
+ virtio_mem_send_response_simple(vmem, elem, type);
+}
+
+static void virtio_mem_resize_usable_region(VirtIOMEM *vmem,
+ uint64_t requested_size,
+ bool can_shrink)
+{
+ uint64_t newsize = MIN(memory_region_size(&vmem->memdev->mr),
+ requested_size + VIRTIO_MEM_USABLE_EXTENT);
+
+ if (!requested_size) {
+ newsize = 0;
+ }
+
+ if (newsize < vmem->usable_region_size && !can_shrink) {
+ return;
+ }
+
+ vmem->usable_region_size = newsize;
+}
+
+static int virtio_mem_unplug_all(VirtIOMEM *vmem)
+{
+ RAMBlock *rb = vmem->memdev->mr.ram_block;
+ int ret;
+
+ if (virtio_mem_is_busy()) {
+ return -EBUSY;
+ }
+
+ ret = ram_block_discard_range(rb, 0, qemu_ram_get_used_length(rb));
+ if (ret) {
+ error_report("Unexpected error discarding RAM: %s", strerror(-ret));
+ return -EBUSY;
+ }
+ bitmap_clear(vmem->bitmap, 0, vmem->bitmap_size);
+ vmem->size = 0;
+
+ virtio_mem_resize_usable_region(vmem, vmem->requested_size, true);
+ return 0;
+}
+
+static void virtio_mem_unplug_all_request(VirtIOMEM *vmem,
+ VirtQueueElement *elem)
+{
+ if (virtio_mem_unplug_all(vmem)) {
+ virtio_mem_send_response_simple(vmem, elem, VIRTIO_MEM_RESP_BUSY);
+ } else {
+ virtio_mem_send_response_simple(vmem, elem, VIRTIO_MEM_RESP_ACK);
+ }
+}
+
+static void virtio_mem_state_request(VirtIOMEM *vmem, VirtQueueElement *elem,
+ struct virtio_mem_req *req)
+{
+ const uint16_t nb_blocks = le16_to_cpu(req->u.state.nb_blocks);
+ const uint64_t gpa = le64_to_cpu(req->u.state.addr);
+ const uint64_t size = nb_blocks * vmem->block_size;
+ struct virtio_mem_resp resp = {
+ .type = cpu_to_le16(VIRTIO_MEM_RESP_ACK),
+ };
+
+ if (!virtio_mem_valid_range(vmem, gpa, size)) {
+ virtio_mem_send_response_simple(vmem, elem, VIRTIO_MEM_RESP_ERROR);
+ return;
+ }
+
+ if (virtio_mem_test_bitmap(vmem, gpa, size, true)) {
+ resp.u.state.state = cpu_to_le16(VIRTIO_MEM_STATE_PLUGGED);
+ } else if (virtio_mem_test_bitmap(vmem, gpa, size, false)) {
+ resp.u.state.state = cpu_to_le16(VIRTIO_MEM_STATE_UNPLUGGED);
+ } else {
+ resp.u.state.state = cpu_to_le16(VIRTIO_MEM_STATE_MIXED);
+ }
+ virtio_mem_send_response(vmem, elem, &resp);
+}
+
+static void virtio_mem_handle_request(VirtIODevice *vdev, VirtQueue *vq)
+{
+ const int len = sizeof(struct virtio_mem_req);
+ VirtIOMEM *vmem = VIRTIO_MEM(vdev);
+ VirtQueueElement *elem;
+ struct virtio_mem_req req;
+ uint16_t type;
+
+ while (true) {
+ elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
+ if (!elem) {
+ return;
+ }
+
+ 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);
+ g_free(elem);
+ return;
+ }
+
+ if (iov_size(elem->in_sg, elem->in_num) <
+ sizeof(struct virtio_mem_resp)) {
+ virtio_error(vdev, "virtio-mem protocol violation: not enough space"
+ " for response: %zu",
+ iov_size(elem->in_sg, elem->in_num));
+ g_free(elem);
+ return;
+ }
+
+ type = le16_to_cpu(req.type);
+ switch (type) {
+ case VIRTIO_MEM_REQ_PLUG:
+ virtio_mem_plug_request(vmem, elem, &req);
+ break;
+ case VIRTIO_MEM_REQ_UNPLUG:
+ virtio_mem_unplug_request(vmem, elem, &req);
+ break;
+ case VIRTIO_MEM_REQ_UNPLUG_ALL:
+ virtio_mem_unplug_all_request(vmem, elem);
+ break;
+ case VIRTIO_MEM_REQ_STATE:
+ virtio_mem_state_request(vmem, elem, &req);
+ break;
+ default:
+ virtio_error(vdev, "virtio-mem protocol violation: unknown request"
+ " type: %d", type);
+ g_free(elem);
+ return;
+ }
+
+ g_free(elem);
+ }
+}
+
+static void virtio_mem_get_config(VirtIODevice *vdev, uint8_t *config_data)
+{
+ VirtIOMEM *vmem = VIRTIO_MEM(vdev);
+ struct virtio_mem_config *config = (void *) config_data;
+
+ config->block_size = cpu_to_le64(vmem->block_size);
+ config->node_id = cpu_to_le16(vmem->node);
+ config->requested_size = cpu_to_le64(vmem->requested_size);
+ config->plugged_size = cpu_to_le64(vmem->size);
+ config->addr = cpu_to_le64(vmem->addr);
+ config->region_size = cpu_to_le64(memory_region_size(&vmem->memdev->mr));
+ config->usable_region_size = cpu_to_le64(vmem->usable_region_size);
+}
+
+static uint64_t virtio_mem_get_features(VirtIODevice *vdev, uint64_t features,
+ Error **errp)
+{
+ MachineState *ms = MACHINE(qdev_get_machine());
+
+ if (ms->numa_state) {
+#if defined(CONFIG_ACPI)
+ virtio_add_feature(&features, VIRTIO_MEM_F_ACPI_PXM);
+#endif
+ }
+ return features;
+}
+
+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_device_realize(DeviceState *dev, Error **errp)
+{
+ MachineState *ms = MACHINE(qdev_get_machine());
+ int nb_numa_nodes = ms->numa_state ? ms->numa_state->num_nodes : 0;
+ VirtIODevice *vdev = VIRTIO_