[POC PATCH 3/5] memory/guest_memfd: Enable in-place conversion when available

Xiaoyao Li posted 5 patches 4 months ago
Maintainers: Paolo Bonzini <pbonzini@redhat.com>, Peter Xu <peterx@redhat.com>, David Hildenbrand <david@redhat.com>, "Philippe Mathieu-Daudé" <philmd@linaro.org>, "Michael S. Tsirkin" <mst@redhat.com>, Cornelia Huck <cohuck@redhat.com>
[POC PATCH 3/5] memory/guest_memfd: Enable in-place conversion when available
Posted by Xiaoyao Li 4 months ago
From: Yan Zhao <yan.y.zhao@intel.com>

(This is just the POC code to use in-place conversion gmem.)

Try to use in-place conversion gmem when it is supported.

When in-place conversion is enabled, there is no need to discard memory
since it still needs to be used as the memory of opposite attribute
after conversion.

For a upstreamable solution, we can introduce memory-backend-guestmemfd
for in-place conversion. With the non in-place conversion, it needs
seperate non-gmem memory to back the shared memory and gmem is created
implicitly and internally based on vm type. While with in-place
conversion, there is no need for seperate non-gmem memory because gmem
itself can be served as shared memory. So that we can introduce
memory-backend-guestmemfd as the specific backend for in-place
conversion gmem.

Signed-off-by: Yan Zhao <yan.y.zhao@intel.com>
Co-developed-by Xiaoyao Li <xiaoyao.li@intel.com>
Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
---
 accel/kvm/kvm-all.c       | 79 ++++++++++++++++++++++++++++-----------
 accel/stubs/kvm-stub.c    |  1 +
 include/system/kvm.h      |  1 +
 include/system/memory.h   |  2 +
 include/system/ramblock.h |  1 +
 system/memory.c           |  7 ++++
 system/physmem.c          | 21 ++++++++++-
 7 files changed, 90 insertions(+), 22 deletions(-)

diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
index a106d1ba0f0b..609537738d38 100644
--- a/accel/kvm/kvm-all.c
+++ b/accel/kvm/kvm-all.c
@@ -105,6 +105,7 @@ static int kvm_sstep_flags;
 static bool kvm_immediate_exit;
 static uint64_t kvm_supported_memory_attributes;
 static bool kvm_guest_memfd_supported;
+bool kvm_guest_memfd_inplace_supported;
 static hwaddr kvm_max_slot_size = ~0;
 
 static const KVMCapabilityInfo kvm_required_capabilites[] = {
@@ -1487,6 +1488,30 @@ static int kvm_set_memory_attributes(hwaddr start, uint64_t size, uint64_t attr)
     return r;
 }
 
+static int kvm_set_guest_memfd_shareability(MemoryRegion *mr, ram_addr_t offset,
+                                            uint64_t size, bool shared)
+{
+    int guest_memfd = mr->ram_block->guest_memfd;
+    struct kvm_gmem_convert param = {
+                .offset = offset,
+                .size = size,
+                .error_offset = 0,
+    };
+    unsigned long op;
+    int r;
+
+    op = shared ? KVM_GMEM_CONVERT_SHARED : KVM_GMEM_CONVERT_PRIVATE;
+
+    r = ioctl(guest_memfd, op, &param);
+    if (r) {
+        error_report("failed to set guest_memfd offset 0x%lx size 0x%lx to %s  "
+                     "error '%s' error offset 0x%llx",
+                     offset, size, shared ? "shared" : "private",
+                     strerror(errno), param.error_offset);
+    }
+    return r;
+}
+
 int kvm_set_memory_attributes_private(hwaddr start, uint64_t size)
 {
     return kvm_set_memory_attributes(start, size, KVM_MEMORY_ATTRIBUTE_PRIVATE);
@@ -1604,7 +1629,8 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
             abort();
         }
 
-        if (memory_region_has_guest_memfd(mr)) {
+        if (memory_region_has_guest_memfd(mr) &&
+            !memory_region_guest_memfd_in_place_conversion(mr)) {
             err = kvm_set_memory_attributes_private(start_addr, slot_size);
             if (err) {
                 error_report("%s: failed to set memory attribute private: %s",
@@ -2779,6 +2805,9 @@ static int kvm_init(AccelState *as, MachineState *ms)
         kvm_check_extension(s, KVM_CAP_GUEST_MEMFD) &&
         kvm_check_extension(s, KVM_CAP_USER_MEMORY2) &&
         (kvm_supported_memory_attributes & KVM_MEMORY_ATTRIBUTE_PRIVATE);
+    kvm_guest_memfd_inplace_supported =
+        kvm_check_extension(s, KVM_CAP_GMEM_SHARED_MEM) &&
+        kvm_check_extension(s, KVM_CAP_GMEM_CONVERSION);
     kvm_pre_fault_memory_supported = kvm_vm_check_extension(s, KVM_CAP_PRE_FAULT_MEMORY);
 
     if (s->kernel_irqchip_split == ON_OFF_AUTO_AUTO) {
@@ -3056,6 +3085,7 @@ static void kvm_eat_signals(CPUState *cpu)
 
 int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private)
 {
+    bool in_place_conversion = false;
     MemoryRegionSection section;
     ram_addr_t offset;
     MemoryRegion *mr;
@@ -3112,18 +3142,23 @@ int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private)
         goto out_unref;
     }
 
-    if (to_private) {
-        ret = kvm_set_memory_attributes_private(start, size);
-    } else {
-        ret = kvm_set_memory_attributes_shared(start, size);
-    }
-    if (ret) {
-        goto out_unref;
-    }
-
     addr = memory_region_get_ram_ptr(mr) + section.offset_within_region;
     rb = qemu_ram_block_from_host(addr, false, &offset);
 
+    in_place_conversion = memory_region_guest_memfd_in_place_conversion(mr);
+    if (in_place_conversion) {
+        ret = kvm_set_guest_memfd_shareability(mr, offset, size, !to_private);
+    } else {
+        if (to_private) {
+            ret = kvm_set_memory_attributes_private(start, size);
+        } else {
+            ret = kvm_set_memory_attributes_shared(start, size);
+        }
+    }
+    if (ret) {
+        goto out_unref;
+    }
+
     ret = ram_block_attributes_state_change(RAM_BLOCK_ATTRIBUTES(mr->rdm),
                                             offset, size, to_private);
     if (ret) {
@@ -3133,17 +3168,19 @@ int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private)
         goto out_unref;
     }
 
-    if (to_private) {
-        if (rb->page_size != qemu_real_host_page_size()) {
-            /*
-             * shared memory is backed by hugetlb, which is supposed to be
-             * pre-allocated and doesn't need to be discarded
-             */
-            goto out_unref;
-        }
-        ret = ram_block_discard_range(rb, offset, size);
-    } else {
-        ret = ram_block_discard_guest_memfd_range(rb, offset, size);
+    if (!in_place_conversion) {
+        if (to_private) {
+            if (rb->page_size != qemu_real_host_page_size()) {
+               /*
+                * shared memory is backed by hugetlb, which is supposed to be
+                * pre-allocated and doesn't need to be discarded
+                */
+                goto out_unref;
+             }
+             ret = ram_block_discard_range(rb, offset, size);
+         } else {
+             ret = ram_block_discard_guest_memfd_range(rb, offset, size);
+         }
     }
 
 out_unref:
diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c
index 68cd33ba9735..bf0ccae27b62 100644
--- a/accel/stubs/kvm-stub.c
+++ b/accel/stubs/kvm-stub.c
@@ -24,6 +24,7 @@ bool kvm_gsi_direct_mapping;
 bool kvm_allowed;
 bool kvm_readonly_mem_allowed;
 bool kvm_msi_use_devid;
+bool kvm_guest_memfd_inplace_supported;
 
 void kvm_flush_coalesced_mmio_buffer(void)
 {
diff --git a/include/system/kvm.h b/include/system/kvm.h
index 3c7d31473663..32f2be5f92e1 100644
--- a/include/system/kvm.h
+++ b/include/system/kvm.h
@@ -43,6 +43,7 @@ extern bool kvm_gsi_direct_mapping;
 extern bool kvm_readonly_mem_allowed;
 extern bool kvm_msi_use_devid;
 extern bool kvm_pre_fault_memory_supported;
+extern bool kvm_guest_memfd_inplace_supported;
 
 #define kvm_enabled()           (kvm_allowed)
 /**
diff --git a/include/system/memory.h b/include/system/memory.h
index 46248d4a52c4..f14fbf65805d 100644
--- a/include/system/memory.h
+++ b/include/system/memory.h
@@ -1812,6 +1812,8 @@ bool memory_region_is_protected(MemoryRegion *mr);
  */
 bool memory_region_has_guest_memfd(MemoryRegion *mr);
 
+bool memory_region_guest_memfd_in_place_conversion(MemoryRegion *mr);
+
 /**
  * memory_region_get_iommu: check whether a memory region is an iommu
  *
diff --git a/include/system/ramblock.h b/include/system/ramblock.h
index 87e847e184aa..87757940ea21 100644
--- a/include/system/ramblock.h
+++ b/include/system/ramblock.h
@@ -46,6 +46,7 @@ struct RAMBlock {
     int fd;
     uint64_t fd_offset;
     int guest_memfd;
+    uint64_t guest_memfd_flags;
     RamBlockAttributes *attributes;
     size_t page_size;
     /* dirty bitmap used during migration */
diff --git a/system/memory.c b/system/memory.c
index e8d9b15b28f6..6870a41629ef 100644
--- a/system/memory.c
+++ b/system/memory.c
@@ -35,6 +35,7 @@
 
 #include "memory-internal.h"
 
+#include <linux/kvm.h>
 //#define DEBUG_UNASSIGNED
 
 static unsigned memory_region_transaction_depth;
@@ -1878,6 +1879,12 @@ bool memory_region_has_guest_memfd(MemoryRegion *mr)
     return mr->ram_block && mr->ram_block->guest_memfd >= 0;
 }
 
+bool memory_region_guest_memfd_in_place_conversion(MemoryRegion *mr)
+{
+    return mr && memory_region_has_guest_memfd(mr) &&
+           (mr->ram_block->guest_memfd_flags & GUEST_MEMFD_FLAG_SUPPORT_SHARED);
+}
+
 uint8_t memory_region_get_dirty_log_mask(MemoryRegion *mr)
 {
     uint8_t mask = mr->dirty_log_mask;
diff --git a/system/physmem.c b/system/physmem.c
index 130c148ffb5c..955480685310 100644
--- a/system/physmem.c
+++ b/system/physmem.c
@@ -89,6 +89,9 @@
 
 #include "memory-internal.h"
 
+#include <linux/guestmem.h>
+#include <linux/kvm.h>
+
 //#define DEBUG_SUBPAGE
 
 /* ram_list is read under rcu_read_lock()/rcu_read_unlock().  Writes
@@ -1913,6 +1916,9 @@ static void ram_block_add(RAMBlock *new_block, Error **errp)
 
     if (new_block->flags & RAM_GUEST_MEMFD) {
         int ret;
+        bool in_place = kvm_guest_memfd_inplace_supported;
+
+        new_block->guest_memfd_flags = 0;
 
         if (!kvm_enabled()) {
             error_setg(errp, "cannot set up private guest memory for %s: KVM required",
@@ -1929,13 +1935,26 @@ static void ram_block_add(RAMBlock *new_block, Error **errp)
             goto out_free;
         }
 
+        if (in_place) {
+            new_block->guest_memfd_flags |= GUEST_MEMFD_FLAG_SUPPORT_SHARED |
+                                            GUEST_MEMFD_FLAG_INIT_PRIVATE;
+        }
+
         new_block->guest_memfd = kvm_create_guest_memfd(new_block->max_length,
-                                                        0, errp);
+                                 new_block->guest_memfd_flags, errp);
         if (new_block->guest_memfd < 0) {
             qemu_mutex_unlock_ramlist();
             goto out_free;
         }
 
+        if (in_place) {
+            qemu_ram_munmap(new_block->fd, new_block->host, new_block->max_length);
+            new_block->host = qemu_ram_mmap(new_block->guest_memfd,
+                                            new_block->max_length,
+                                            QEMU_VMALLOC_ALIGN,
+                                            QEMU_MAP_SHARED, 0);
+        }
+
         /*
          * The attribute bitmap of the RamBlockAttributes is default to
          * discarded, which mimics the behavior of kvm_set_phys_mem() when it
-- 
2.43.0
Re: [POC PATCH 3/5] memory/guest_memfd: Enable in-place conversion when available
Posted by Chenyi Qiang 4 months ago

On 7/15/2025 11:31 AM, Xiaoyao Li wrote:
> From: Yan Zhao <yan.y.zhao@intel.com>
> 
> (This is just the POC code to use in-place conversion gmem.)
> 
> Try to use in-place conversion gmem when it is supported.
> 
> When in-place conversion is enabled, there is no need to discard memory
> since it still needs to be used as the memory of opposite attribute
> after conversion.
> 
> For a upstreamable solution, we can introduce memory-backend-guestmemfd
> for in-place conversion. With the non in-place conversion, it needs
> seperate non-gmem memory to back the shared memory and gmem is created
> implicitly and internally based on vm type. While with in-place
> conversion, there is no need for seperate non-gmem memory because gmem
> itself can be served as shared memory. So that we can introduce
> memory-backend-guestmemfd as the specific backend for in-place
> conversion gmem.
> 
> Signed-off-by: Yan Zhao <yan.y.zhao@intel.com>
> Co-developed-by Xiaoyao Li <xiaoyao.li@intel.com>
> Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
> ---
>  accel/kvm/kvm-all.c       | 79 ++++++++++++++++++++++++++++-----------
>  accel/stubs/kvm-stub.c    |  1 +
>  include/system/kvm.h      |  1 +
>  include/system/memory.h   |  2 +
>  include/system/ramblock.h |  1 +
>  system/memory.c           |  7 ++++
>  system/physmem.c          | 21 ++++++++++-
>  7 files changed, 90 insertions(+), 22 deletions(-)
> 
> diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
> index a106d1ba0f0b..609537738d38 100644
> --- a/accel/kvm/kvm-all.c
> +++ b/accel/kvm/kvm-all.c
> @@ -105,6 +105,7 @@ static int kvm_sstep_flags;
>  static bool kvm_immediate_exit;
>  static uint64_t kvm_supported_memory_attributes;
>  static bool kvm_guest_memfd_supported;
> +bool kvm_guest_memfd_inplace_supported;
>  static hwaddr kvm_max_slot_size = ~0;
>  
>  static const KVMCapabilityInfo kvm_required_capabilites[] = {
> @@ -1487,6 +1488,30 @@ static int kvm_set_memory_attributes(hwaddr start, uint64_t size, uint64_t attr)
>      return r;
>  }
>  
> +static int kvm_set_guest_memfd_shareability(MemoryRegion *mr, ram_addr_t offset,
> +                                            uint64_t size, bool shared)
> +{
> +    int guest_memfd = mr->ram_block->guest_memfd;
> +    struct kvm_gmem_convert param = {
> +                .offset = offset,
> +                .size = size,
> +                .error_offset = 0,
> +    };
> +    unsigned long op;
> +    int r;
> +
> +    op = shared ? KVM_GMEM_CONVERT_SHARED : KVM_GMEM_CONVERT_PRIVATE;
> +
> +    r = ioctl(guest_memfd, op, &param);
> +    if (r) {
> +        error_report("failed to set guest_memfd offset 0x%lx size 0x%lx to %s  "
> +                     "error '%s' error offset 0x%llx",
> +                     offset, size, shared ? "shared" : "private",
> +                     strerror(errno), param.error_offset);
> +    }
> +    return r;
> +}
> +
>  int kvm_set_memory_attributes_private(hwaddr start, uint64_t size)
>  {
>      return kvm_set_memory_attributes(start, size, KVM_MEMORY_ATTRIBUTE_PRIVATE);
> @@ -1604,7 +1629,8 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
>              abort();
>          }
>  
> -        if (memory_region_has_guest_memfd(mr)) {
> +        if (memory_region_has_guest_memfd(mr) &&
> +            !memory_region_guest_memfd_in_place_conversion(mr)) {
>              err = kvm_set_memory_attributes_private(start_addr, slot_size);
>              if (err) {
>                  error_report("%s: failed to set memory attribute private: %s",
> @@ -2779,6 +2805,9 @@ static int kvm_init(AccelState *as, MachineState *ms)
>          kvm_check_extension(s, KVM_CAP_GUEST_MEMFD) &&
>          kvm_check_extension(s, KVM_CAP_USER_MEMORY2) &&
>          (kvm_supported_memory_attributes & KVM_MEMORY_ATTRIBUTE_PRIVATE);
> +    kvm_guest_memfd_inplace_supported =
> +        kvm_check_extension(s, KVM_CAP_GMEM_SHARED_MEM) &&
> +        kvm_check_extension(s, KVM_CAP_GMEM_CONVERSION);
>      kvm_pre_fault_memory_supported = kvm_vm_check_extension(s, KVM_CAP_PRE_FAULT_MEMORY);
>  
>      if (s->kernel_irqchip_split == ON_OFF_AUTO_AUTO) {
> @@ -3056,6 +3085,7 @@ static void kvm_eat_signals(CPUState *cpu)
>  
>  int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private)
>  {
> +    bool in_place_conversion = false;
>      MemoryRegionSection section;
>      ram_addr_t offset;
>      MemoryRegion *mr;
> @@ -3112,18 +3142,23 @@ int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private)
>          goto out_unref;
>      }
>  
> -    if (to_private) {
> -        ret = kvm_set_memory_attributes_private(start, size);
> -    } else {
> -        ret = kvm_set_memory_attributes_shared(start, size);
> -    }
> -    if (ret) {
> -        goto out_unref;
> -    }
> -
>      addr = memory_region_get_ram_ptr(mr) + section.offset_within_region;
>      rb = qemu_ram_block_from_host(addr, false, &offset);
>  
> +    in_place_conversion = memory_region_guest_memfd_in_place_conversion(mr);
> +    if (in_place_conversion) {
> +        ret = kvm_set_guest_memfd_shareability(mr, offset, size, !to_private);
> +    } else {
> +        if (to_private) {
> +            ret = kvm_set_memory_attributes_private(start, size);
> +        } else {
> +            ret = kvm_set_memory_attributes_shared(start, size);
> +        }
> +    }
> +    if (ret) {
> +        goto out_unref;
> +    }
> +
>      ret = ram_block_attributes_state_change(RAM_BLOCK_ATTRIBUTES(mr->rdm),
>                                              offset, size, to_private);
>      if (ret) {

There's one thing required for shared device assignment with in-place conversion, we need to follow the
sequence of unmap-before-conversion-to-private and map-after-conversion-to-shared. Maybe change it like:

diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
index a54e68e769..e9e62ae8f2 100644
--- a/accel/kvm/kvm-all.c
+++ b/accel/kvm/kvm-all.c
@@ -3146,6 +3146,17 @@ int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private)
     addr = memory_region_get_ram_ptr(mr) + section.offset_within_region;
     rb = qemu_ram_block_from_host(addr, false, &offset);
 
+    if (to_private) {
+        ret = ram_block_attributes_state_change(RAM_BLOCK_ATTRIBUTES(mr->rdm),
+                                                offset, size, to_private);
+        if (ret) {
+            error_report("Failed to notify the listener the state change of "
+                         "(0x%"HWADDR_PRIx" + 0x%"HWADDR_PRIx") to %s",
+                         start, size, to_private ? "private" : "shared");
+            goto out_unref;
+        }
+    }
+
     in_place_conversion = memory_region_guest_memfd_in_place_conversion(mr);
     if (in_place_conversion) {
         ret = kvm_set_guest_memfd_shareability(mr, offset, size, !to_private);
@@ -3160,13 +3171,15 @@ int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private)
         goto out_unref;
     }
 
-    ret = ram_block_attributes_state_change(RAM_BLOCK_ATTRIBUTES(mr->rdm),
-                                            offset, size, to_private);
-    if (ret) {
-        error_report("Failed to notify the listener the state change of "
-                     "(0x%"HWADDR_PRIx" + 0x%"HWADDR_PRIx") to %s",
-                     start, size, to_private ? "private" : "shared");
-        goto out_unref;
+    if (!to_private) {
+        ret = ram_block_attributes_state_change(RAM_BLOCK_ATTRIBUTES(mr->rdm),
+                                                offset, size, to_private);
+        if (ret) {
+            error_report("Failed to notify the listener the state change of "
+                         "(0x%"HWADDR_PRIx" + 0x%"HWADDR_PRIx") to %s",
+                         start, size, to_private ? "private" : "shared");
+            goto out_unref;
+        }
     }
Re: [POC PATCH 3/5] memory/guest_memfd: Enable in-place conversion when available
Posted by Xiaoyao Li 3 months, 2 weeks ago
On 7/17/2025 10:02 AM, Chenyi Qiang wrote:
> 
> 
> On 7/15/2025 11:31 AM, Xiaoyao Li wrote:
>> From: Yan Zhao <yan.y.zhao@intel.com>
>>
>> (This is just the POC code to use in-place conversion gmem.)
>>
>> Try to use in-place conversion gmem when it is supported.
>>
>> When in-place conversion is enabled, there is no need to discard memory
>> since it still needs to be used as the memory of opposite attribute
>> after conversion.
>>
>> For a upstreamable solution, we can introduce memory-backend-guestmemfd
>> for in-place conversion. With the non in-place conversion, it needs
>> seperate non-gmem memory to back the shared memory and gmem is created
>> implicitly and internally based on vm type. While with in-place
>> conversion, there is no need for seperate non-gmem memory because gmem
>> itself can be served as shared memory. So that we can introduce
>> memory-backend-guestmemfd as the specific backend for in-place
>> conversion gmem.
>>
>> Signed-off-by: Yan Zhao <yan.y.zhao@intel.com>
>> Co-developed-by Xiaoyao Li <xiaoyao.li@intel.com>
>> Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
>> ---
>>   accel/kvm/kvm-all.c       | 79 ++++++++++++++++++++++++++++-----------
>>   accel/stubs/kvm-stub.c    |  1 +
>>   include/system/kvm.h      |  1 +
>>   include/system/memory.h   |  2 +
>>   include/system/ramblock.h |  1 +
>>   system/memory.c           |  7 ++++
>>   system/physmem.c          | 21 ++++++++++-
>>   7 files changed, 90 insertions(+), 22 deletions(-)
>>
>> diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
>> index a106d1ba0f0b..609537738d38 100644
>> --- a/accel/kvm/kvm-all.c
>> +++ b/accel/kvm/kvm-all.c
>> @@ -105,6 +105,7 @@ static int kvm_sstep_flags;
>>   static bool kvm_immediate_exit;
>>   static uint64_t kvm_supported_memory_attributes;
>>   static bool kvm_guest_memfd_supported;
>> +bool kvm_guest_memfd_inplace_supported;
>>   static hwaddr kvm_max_slot_size = ~0;
>>   
>>   static const KVMCapabilityInfo kvm_required_capabilites[] = {
>> @@ -1487,6 +1488,30 @@ static int kvm_set_memory_attributes(hwaddr start, uint64_t size, uint64_t attr)
>>       return r;
>>   }
>>   
>> +static int kvm_set_guest_memfd_shareability(MemoryRegion *mr, ram_addr_t offset,
>> +                                            uint64_t size, bool shared)
>> +{
>> +    int guest_memfd = mr->ram_block->guest_memfd;
>> +    struct kvm_gmem_convert param = {
>> +                .offset = offset,
>> +                .size = size,
>> +                .error_offset = 0,
>> +    };
>> +    unsigned long op;
>> +    int r;
>> +
>> +    op = shared ? KVM_GMEM_CONVERT_SHARED : KVM_GMEM_CONVERT_PRIVATE;
>> +
>> +    r = ioctl(guest_memfd, op, &param);
>> +    if (r) {
>> +        error_report("failed to set guest_memfd offset 0x%lx size 0x%lx to %s  "
>> +                     "error '%s' error offset 0x%llx",
>> +                     offset, size, shared ? "shared" : "private",
>> +                     strerror(errno), param.error_offset);
>> +    }
>> +    return r;
>> +}
>> +
>>   int kvm_set_memory_attributes_private(hwaddr start, uint64_t size)
>>   {
>>       return kvm_set_memory_attributes(start, size, KVM_MEMORY_ATTRIBUTE_PRIVATE);
>> @@ -1604,7 +1629,8 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
>>               abort();
>>           }
>>   
>> -        if (memory_region_has_guest_memfd(mr)) {
>> +        if (memory_region_has_guest_memfd(mr) &&
>> +            !memory_region_guest_memfd_in_place_conversion(mr)) {
>>               err = kvm_set_memory_attributes_private(start_addr, slot_size);
>>               if (err) {
>>                   error_report("%s: failed to set memory attribute private: %s",
>> @@ -2779,6 +2805,9 @@ static int kvm_init(AccelState *as, MachineState *ms)
>>           kvm_check_extension(s, KVM_CAP_GUEST_MEMFD) &&
>>           kvm_check_extension(s, KVM_CAP_USER_MEMORY2) &&
>>           (kvm_supported_memory_attributes & KVM_MEMORY_ATTRIBUTE_PRIVATE);
>> +    kvm_guest_memfd_inplace_supported =
>> +        kvm_check_extension(s, KVM_CAP_GMEM_SHARED_MEM) &&
>> +        kvm_check_extension(s, KVM_CAP_GMEM_CONVERSION);
>>       kvm_pre_fault_memory_supported = kvm_vm_check_extension(s, KVM_CAP_PRE_FAULT_MEMORY);
>>   
>>       if (s->kernel_irqchip_split == ON_OFF_AUTO_AUTO) {
>> @@ -3056,6 +3085,7 @@ static void kvm_eat_signals(CPUState *cpu)
>>   
>>   int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private)
>>   {
>> +    bool in_place_conversion = false;
>>       MemoryRegionSection section;
>>       ram_addr_t offset;
>>       MemoryRegion *mr;
>> @@ -3112,18 +3142,23 @@ int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private)
>>           goto out_unref;
>>       }
>>   
>> -    if (to_private) {
>> -        ret = kvm_set_memory_attributes_private(start, size);
>> -    } else {
>> -        ret = kvm_set_memory_attributes_shared(start, size);
>> -    }
>> -    if (ret) {
>> -        goto out_unref;
>> -    }
>> -
>>       addr = memory_region_get_ram_ptr(mr) + section.offset_within_region;
>>       rb = qemu_ram_block_from_host(addr, false, &offset);
>>   
>> +    in_place_conversion = memory_region_guest_memfd_in_place_conversion(mr);
>> +    if (in_place_conversion) {
>> +        ret = kvm_set_guest_memfd_shareability(mr, offset, size, !to_private);
>> +    } else {
>> +        if (to_private) {
>> +            ret = kvm_set_memory_attributes_private(start, size);
>> +        } else {
>> +            ret = kvm_set_memory_attributes_shared(start, size);
>> +        }
>> +    }
>> +    if (ret) {
>> +        goto out_unref;
>> +    }
>> +
>>       ret = ram_block_attributes_state_change(RAM_BLOCK_ATTRIBUTES(mr->rdm),
>>                                               offset, size, to_private);
>>       if (ret) {
> 
> There's one thing required for shared device assignment with in-place conversion, we need to follow the
> sequence of unmap-before-conversion-to-private and map-after-conversion-to-shared. Maybe change it like:
> 
> diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
> index a54e68e769..e9e62ae8f2 100644
> --- a/accel/kvm/kvm-all.c
> +++ b/accel/kvm/kvm-all.c
> @@ -3146,6 +3146,17 @@ int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private)
>       addr = memory_region_get_ram_ptr(mr) + section.offset_within_region;
>       rb = qemu_ram_block_from_host(addr, false, &offset);
>   
> +    if (to_private) {
> +        ret = ram_block_attributes_state_change(RAM_BLOCK_ATTRIBUTES(mr->rdm),
> +                                                offset, size, to_private);
> +        if (ret) {
> +            error_report("Failed to notify the listener the state change of "
> +                         "(0x%"HWADDR_PRIx" + 0x%"HWADDR_PRIx") to %s",
> +                         start, size, to_private ? "private" : "shared");
> +            goto out_unref;
> +        }
> +    }
> +
>       in_place_conversion = memory_region_guest_memfd_in_place_conversion(mr);
>       if (in_place_conversion) {
>           ret = kvm_set_guest_memfd_shareability(mr, offset, size, !to_private);
> @@ -3160,13 +3171,15 @@ int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private)
>           goto out_unref;
>       }
>   
> -    ret = ram_block_attributes_state_change(RAM_BLOCK_ATTRIBUTES(mr->rdm),
> -                                            offset, size, to_private);
> -    if (ret) {
> -        error_report("Failed to notify the listener the state change of "
> -                     "(0x%"HWADDR_PRIx" + 0x%"HWADDR_PRIx") to %s",
> -                     start, size, to_private ? "private" : "shared");
> -        goto out_unref;
> +    if (!to_private) {
> +        ret = ram_block_attributes_state_change(RAM_BLOCK_ATTRIBUTES(mr->rdm),
> +                                                offset, size, to_private);
> +        if (ret) {
> +            error_report("Failed to notify the listener the state change of "
> +                         "(0x%"HWADDR_PRIx" + 0x%"HWADDR_PRIx") to %s",
> +                         start, size, to_private ? "private" : "shared");
> +            goto out_unref;
> +        }
>       }

(Sorry for forgetting to reply in the community)

Thanks for catching and reporting it. I have incorporated it to the 
internal branch.