[PATCH v3 4/9] KVM: guest_memfd: add GUEST_MEMFD_FLAG_MIGRATABLE
Shivank Garg <[email protected]> Wed, 5 Aug 2026 06:40:33 +0000
| Newsgroups | gmane.linux.documentation,gmane.linux.file-systems,gmane.linux.kernel.mm,gmane.linux.kernel,gmane.comp.emulators.kvm.devel |
|---|---|
| Message-ID | <[email protected]> |
guest_memfd folios are currently always marked unmovable, 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 unmovable behavior. Remove redundant WARN_ON_ONCE for checking unevictable mapping. Signed-off-by: Shivank Garg <[email protected]> --- Documentation/virt/kvm/api.rst | 3 +++ 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 ++++++++++++++---- 6 files changed, 32 insertions(+), 4 deletions(-) diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index e3003a241d5b..976921527ef9 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -6461,6 +6461,9 @@ specified via KVM_CREATE_GUEST_MEMFD. Currently defined flags: without INIT_SHARED will be marked private). Shared memory can be faulted into host userspace page tables. Private memory cannot. + GUEST_MEMFD_FLAG_MIGRATABLE Allow MM to migrate guest_memfd folios. + Availability is architecture sepecific. Without + this flag, folios remain unmovable. ============================ ================================================ When the KVM MMU performs a PFN lookup to service a guest fault and the backing diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 47cb9eba113b..7087b0b8f973 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -14130,6 +14130,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 583341d593c9..a2b7c2f1c49f 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