guest_memfd folios are currently always marked unmmovable, which
prevents the MM from invoking ->migrate_folio callback in scenarios
like compaction, offlining, etc. This is unavoidable for confidential VMs
(SEV-SNP, TDX), since memory is encrypted and copying it needs firmware
assistance. However, for non-confidential VMs (like firecracker), we
can still migrate the folios.

Add kvm_arch_supports_gmem_migration() so an architecture can control
whether migration is supported. Add GUEST_MEMFD_FLAG_MIGRATABLE so
userspace can opt-in to folio migration.

For opted-in guest_memfds, use GFP_HIGHUSER_MOVABLE so their folios can
be allocated from ZONE_MOVABLE/CMA and grouped by mobility, otherwise
keep the existing unmmovable behavior.

Remove redundant WARN_ON_ONCE for checking unevictable mapping.

Signed-off-by: Shivank Garg <[email protected]>
---
 arch/x86/kvm/x86.c             |  9 +++++++++
 include/linux/kvm_host.h       |  4 ++++
 include/uapi/linux/kvm.h       |  1 +
 tools/include/uapi/linux/kvm.h |  1 +
 virt/kvm/guest_memfd.c         | 18 ++++++++++++++----
 5 files changed, 29 insertions(+), 4 deletions(-)

diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index afcac1042947..4a7016013271 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -14124,6 +14124,15 @@ bool kvm_arch_supports_gmem_init_shared(struct kvm 
*kvm)
        return !kvm_arch_has_private_mem(kvm);
 }
 
+/*
+ * Migration of guest_memfd with private memory is not supported yet
+ * as this may require architecture-specific handling.
+ */
+bool kvm_arch_supports_gmem_migration(struct kvm *kvm)
+{
+       return !kvm_arch_has_private_mem(kvm);
+}
+
 #ifdef CONFIG_HAVE_KVM_ARCH_GMEM_PREPARE
 int kvm_arch_gmem_prepare(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn, int 
max_order)
 {
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index ab8cfaec82d3..7b2f15b21970 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -731,6 +731,7 @@ static inline bool kvm_arch_has_private_mem(struct kvm *kvm)
 
 #ifdef CONFIG_KVM_GUEST_MEMFD
 bool kvm_arch_supports_gmem_init_shared(struct kvm *kvm);
+bool kvm_arch_supports_gmem_migration(struct kvm *kvm);
 
 static inline u64 kvm_gmem_get_supported_flags(struct kvm *kvm)
 {
@@ -738,6 +739,9 @@ static inline u64 kvm_gmem_get_supported_flags(struct kvm 
*kvm)
 
        if (!kvm || kvm_arch_supports_gmem_init_shared(kvm))
                flags |= GUEST_MEMFD_FLAG_INIT_SHARED;
+       if (IS_ENABLED(CONFIG_MIGRATION) &&
+           (!kvm || kvm_arch_supports_gmem_migration(kvm)))
+               flags |= GUEST_MEMFD_FLAG_MIGRATABLE;
 
        return flags;
 }
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 419011097fa8..e1615a62ed78 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1654,6 +1654,7 @@ struct kvm_memory_attributes {
 #define KVM_CREATE_GUEST_MEMFD _IOWR(KVMIO,  0xd4, struct 
kvm_create_guest_memfd)
 #define GUEST_MEMFD_FLAG_MMAP          (1ULL << 0)
 #define GUEST_MEMFD_FLAG_INIT_SHARED   (1ULL << 1)
+#define GUEST_MEMFD_FLAG_MIGRATABLE    (1ULL << 2)
 
 struct kvm_create_guest_memfd {
        __u64 size;
diff --git a/tools/include/uapi/linux/kvm.h b/tools/include/uapi/linux/kvm.h
index 419011097fa8..e1615a62ed78 100644
--- a/tools/include/uapi/linux/kvm.h
+++ b/tools/include/uapi/linux/kvm.h
@@ -1654,6 +1654,7 @@ struct kvm_memory_attributes {
 #define KVM_CREATE_GUEST_MEMFD _IOWR(KVMIO,  0xd4, struct 
kvm_create_guest_memfd)
 #define GUEST_MEMFD_FLAG_MMAP          (1ULL << 0)
 #define GUEST_MEMFD_FLAG_INIT_SHARED   (1ULL << 1)
+#define GUEST_MEMFD_FLAG_MIGRATABLE    (1ULL << 2)
 
 struct kvm_create_guest_memfd {
        __u64 size;
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index ec881cabe8e3..8ae455a6b38d 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -589,6 +589,11 @@ bool __weak kvm_arch_supports_gmem_init_shared(struct kvm 
*kvm)
        return true;
 }
 
+bool __weak kvm_arch_supports_gmem_migration(struct kvm *kvm)
+{
+       return false;
+}
+
 static int __kvm_gmem_create(struct kvm *kvm, loff_t size, u64 flags)
 {
        static const char *name = "[kvm-gmem]";
@@ -623,11 +628,16 @@ static int __kvm_gmem_create(struct kvm *kvm, loff_t 
size, u64 flags)
        inode->i_mapping->a_ops = &kvm_gmem_aops;
        inode->i_mode |= S_IFREG;
        inode->i_size = size;
-       mapping_set_gfp_mask(inode->i_mapping, GFP_HIGHUSER);
        mapping_set_inaccessible(inode->i_mapping);
-       mapping_set_unmovable(inode->i_mapping);
-       /* Unmovable mappings are supposed to be marked unevictable as well. */
-       WARN_ON_ONCE(!mapping_unevictable(inode->i_mapping));
+       /* guest_memfd mappings should be marked unevictable. */
+       mapping_set_unevictable(inode->i_mapping);
+
+       if (flags & GUEST_MEMFD_FLAG_MIGRATABLE) {
+               mapping_set_gfp_mask(inode->i_mapping, GFP_HIGHUSER_MOVABLE);
+       } else {
+               mapping_set_gfp_mask(inode->i_mapping, GFP_HIGHUSER);
+               mapping_set_unmovable(inode->i_mapping);
+       }
 
        GMEM_I(inode)->flags = flags;
 

-- 
2.43.0


Reply via email to