@samitouri / QOSamiQemu / commits / 1f3241bcae

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>

Marc-André Lureau committed Jun 4, 2026 at 17:43 UTC 1f3241bcaeee0df47f72edf6abad71fe36f3b0e7
5 files changed +26 -8
accel/kvm/kvm-all.c
+1 -1
@@ -3422,7 +3422,7 @@ int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private)
3422 */
3423 goto out_unref;
3424 }
3425 - ret = ram_block_discard_range(rb, offset, size);
3425 + ret = ram_block_discard_shared_range(rb, offset, size);
3426 } else {
3427 ret = ram_block_discard_guest_memfd_range(rb, offset, size);
3428 }
include/system/ramblock.h
+2 -1
@@ -103,7 +103,8 @@ struct RamBlockAttributes {
103
104 /* @offset: the offset within the RAMBlock */
105 int ram_block_discard_range(RAMBlock *rb, uint64_t offset, size_t length);
106 -/* @offset: the offset within the RAMBlock */
106 +int ram_block_discard_shared_range(RAMBlock *rb, uint64_t offset,
107 + size_t length);
108 int ram_block_discard_guest_memfd_range(RAMBlock *rb, uint64_t offset,
109 size_t length);
110
system/physmem.c
+21 -4
@@ -4093,7 +4093,7 @@ int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque)
4093 * Returns: 0 on success, none-0 on failure
4094 *
4095 */
4096 -int ram_block_discard_range(RAMBlock *rb, uint64_t offset, size_t length)
4096 +int ram_block_discard_shared_range(RAMBlock *rb, uint64_t offset, size_t length)
4097 {
4098 int ret = -1;
4099
@@ -4142,7 +4142,7 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t offset, size_t length)
4142 * have a MAP_PRIVATE mapping, possibly messing with other
4143 * MAP_PRIVATE/MAP_SHARED mappings. There is no easy way to
4144 * change that behavior whithout violating the promised
4145 - * semantics of ram_block_discard_range().
4145 + * semantics of ram_block_discard_shared_range().
4146 *
4147 * Only warn, because it works as long as nobody else uses that
4148 * file.
@@ -4198,8 +4198,9 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t offset, size_t length)
4198 goto err;
4199 #endif
4200 }
4201 - trace_ram_block_discard_range(rb->idstr, host_startaddr, length,
4202 - need_madvise, need_fallocate, ret);
4201 + trace_ram_block_discard_shared_range(rb->idstr, host_startaddr, length,
4202 + need_madvise, need_fallocate,
4203 + ret);
4204 } else {
4205 error_report("%s: Overrun block '%s' (%" PRIu64 "/%zx/" RAM_ADDR_FMT")",
4206 __func__, rb->idstr, offset, length, rb->max_length);
@@ -4209,6 +4210,22 @@ err:
4210 return ret;
4211 }
4212
4213 +int ram_block_discard_range(RAMBlock *rb, uint64_t offset, size_t length)
4214 +{
4215 + int ret;
4216 +
4217 + ret = ram_block_discard_shared_range(rb, offset, length);
4218 + if (ret) {
4219 + return ret;
4220 + }
4221 +
4222 + if (rb->guest_memfd >= 0) {
4223 + ret = ram_block_discard_guest_memfd_range(rb, offset, length);
4224 + }
4225 +
4226 + return ret;
4227 +}
4228 +
4229 int ram_block_discard_guest_memfd_range(RAMBlock *rb, uint64_t offset,
4230 size_t length)
4231 {
system/trace-events
+1 -1
@@ -32,7 +32,7 @@ global_dirty_changed(unsigned int bitmask) "bitmask 0x%"PRIx32
32 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"
33 find_ram_offset(uint64_t size, uint64_t offset) "size: 0x%" PRIx64 " @ 0x%" PRIx64
34 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
35 -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"
35 +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"
36 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"
37
38 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"
target/i386/kvm/tdx.c
+1 -1
@@ -385,7 +385,7 @@ static void tdx_finalize_vm(Notifier *notifier, void *unused)
385 * KVM_MEMORY_MAPPING. It becomes useless.
386 */
387 ram_block = tdx_guest->tdvf_mr->ram_block;
388 - ram_block_discard_range(ram_block, 0, ram_block->max_length);
388 + ram_block_discard_shared_range(ram_block, 0, ram_block->max_length);
389
390 tdx_vm_ioctl(KVM_TDX_FINALIZE_VM, 0, NULL, &error_fatal);
391 CONFIDENTIAL_GUEST_SUPPORT(tdx_guest)->ready = true;