system/physmem: make ram_block_discard_range() handle guest_memfd

Most callers of ram_block_discard_range() want to discard both the
shared and guest_memfd backing. Only kvm_convert_memory() intentionally
discards a single plane during private/shared conversions.

Rename the current implementation to ram_block_discard_shared_range()
and make ram_block_discard_range() a composite that also discards
guest_memfd when present (rb->guest_memfd >= 0). This ensures callers
like virtio-mem, virtio-balloon, hv-balloon, migration.. reclaim
private pages on discard.

Update kvm_convert_memory() to use the plane-specific
ram_block_discard_shared_range() since it only needs to discard
the shared backing when converting to private.

Likewise, after TDVF image copy, use ram_block_discard_shared_range().

Reviewed-by: Peter Xu <peterx@redhat.com>
Reviewed-by: Xiaoyao Li <xiaoyao.li@intel.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/r/20260604-rdm5-v5-11-5768e6a0943d@redhat.com
Signed-off-by: Peter Xu <peterx@redhat.com>
This commit is contained in:
Marc-André Lureau
2026-06-04 17:43:57 +04:00
committed by Peter Xu
parent c2237aaf6d
commit 1f3241bcae
5 changed files with 26 additions and 8 deletions

View File

@@ -3422,7 +3422,7 @@ int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private)
*/
goto out_unref;
}
ret = ram_block_discard_range(rb, offset, size);
ret = ram_block_discard_shared_range(rb, offset, size);
} else {
ret = ram_block_discard_guest_memfd_range(rb, offset, size);
}

View File

@@ -103,7 +103,8 @@ struct RamBlockAttributes {
/* @offset: the offset within the RAMBlock */
int ram_block_discard_range(RAMBlock *rb, uint64_t offset, size_t length);
/* @offset: the offset within the RAMBlock */
int ram_block_discard_shared_range(RAMBlock *rb, uint64_t offset,
size_t length);
int ram_block_discard_guest_memfd_range(RAMBlock *rb, uint64_t offset,
size_t length);

View File

@@ -4093,7 +4093,7 @@ int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque)
* Returns: 0 on success, none-0 on failure
*
*/
int ram_block_discard_range(RAMBlock *rb, uint64_t offset, size_t length)
int ram_block_discard_shared_range(RAMBlock *rb, uint64_t offset, size_t length)
{
int ret = -1;
@@ -4142,7 +4142,7 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t offset, size_t length)
* have a MAP_PRIVATE mapping, possibly messing with other
* MAP_PRIVATE/MAP_SHARED mappings. There is no easy way to
* change that behavior whithout violating the promised
* semantics of ram_block_discard_range().
* semantics of ram_block_discard_shared_range().
*
* Only warn, because it works as long as nobody else uses that
* file.
@@ -4198,8 +4198,9 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t offset, size_t length)
goto err;
#endif
}
trace_ram_block_discard_range(rb->idstr, host_startaddr, length,
need_madvise, need_fallocate, ret);
trace_ram_block_discard_shared_range(rb->idstr, host_startaddr, length,
need_madvise, need_fallocate,
ret);
} else {
error_report("%s: Overrun block '%s' (%" PRIu64 "/%zx/" RAM_ADDR_FMT")",
__func__, rb->idstr, offset, length, rb->max_length);
@@ -4209,6 +4210,22 @@ err:
return ret;
}
int ram_block_discard_range(RAMBlock *rb, uint64_t offset, size_t length)
{
int ret;
ret = ram_block_discard_shared_range(rb, offset, length);
if (ret) {
return ret;
}
if (rb->guest_memfd >= 0) {
ret = ram_block_discard_guest_memfd_range(rb, offset, length);
}
return ret;
}
int ram_block_discard_guest_memfd_range(RAMBlock *rb, uint64_t offset,
size_t length)
{

View File

@@ -32,7 +32,7 @@ global_dirty_changed(unsigned int bitmask) "bitmask 0x%"PRIx32
address_space_map(void *as, uint64_t addr, uint64_t len, bool is_write, uint32_t attrs) "as:%p addr 0x%"PRIx64":%"PRIx64" write:%d attrs:0x%x"
find_ram_offset(uint64_t size, uint64_t offset) "size: 0x%" PRIx64 " @ 0x%" PRIx64
find_ram_offset_loop(uint64_t size, uint64_t candidate, uint64_t offset, uint64_t next, uint64_t mingap) "trying size: 0x%" PRIx64 " @ 0x%" PRIx64 ", offset: 0x%" PRIx64" next: 0x%" PRIx64 " mingap: 0x%" PRIx64
ram_block_discard_range(const char *rbname, void *hva, size_t length, bool need_madvise, bool need_fallocate, int ret) "%s@%p + 0x%zx: madvise: %d fallocate: %d ret: %d"
ram_block_discard_shared_range(const char *rbname, void *hva, size_t length, bool need_madvise, bool need_fallocate, int ret) "%s@%p + 0x%zx: madvise: %d fallocate: %d ret: %d"
qemu_ram_alloc_shared(const char *name, size_t size, size_t max_size, int fd, void *host) "%s size %zu max_size %zu fd %d host %p"
subpage_register(void *subpage, uint32_t start, uint32_t end, int idx, int eidx, uint16_t section) "subpage %p start 0x%08x end 0x%08x idx 0x%08x eidx 0x%08x section %u"

View File

@@ -385,7 +385,7 @@ static void tdx_finalize_vm(Notifier *notifier, void *unused)
* KVM_MEMORY_MAPPING. It becomes useless.
*/
ram_block = tdx_guest->tdvf_mr->ram_block;
ram_block_discard_range(ram_block, 0, ram_block->max_length);
ram_block_discard_shared_range(ram_block, 0, ram_block->max_length);
tdx_vm_ioctl(KVM_TDX_FINALIZE_VM, 0, NULL, &error_fatal);
CONFIDENTIAL_GUEST_SUPPORT(tdx_guest)->ready = true;