From 1f3241bcaeee0df47f72edf6abad71fe36f3b0e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 4 Jun 2026 17:43:57 +0400 Subject: [PATCH] system/physmem: make ram_block_discard_range() handle guest_memfd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Xiaoyao Li Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20260604-rdm5-v5-11-5768e6a0943d@redhat.com Signed-off-by: Peter Xu --- accel/kvm/kvm-all.c | 2 +- include/system/ramblock.h | 3 ++- system/physmem.c | 25 +++++++++++++++++++++---- system/trace-events | 2 +- target/i386/kvm/tdx.c | 2 +- 5 files changed, 26 insertions(+), 8 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index f4f0e64fbd..8ddfbaff46 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -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); } diff --git a/include/system/ramblock.h b/include/system/ramblock.h index 2b38718fe5..f0639287bf 100644 --- a/include/system/ramblock.h +++ b/include/system/ramblock.h @@ -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); diff --git a/system/physmem.c b/system/physmem.c index c4bfb57625..c21ea92915 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -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) { diff --git a/system/trace-events b/system/trace-events index e6e1b61279..51b4a4679a 100644 --- a/system/trace-events +++ b/system/trace-events @@ -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" diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index df46fce769..dfad469112 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -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;