[PATCH RFC v5 28/53] KVM: Add CAP to enumerate supported SET_MEMORY_ATTRIBUTES2 flags

Ackerley Tng via B4 Relay
2026-04-28T23:25:19Z

Add CAP to enumerate supported SET_MEMORY_ATTRIBUTES2 flags, so userspace
can find out which flags are supported when sending the
KVM_SET_MEMORY_ATTRIBUTES2 ioctl to a guest_memfd.

Add a parameter for_cap to support enumeration of supported flags
irrespective of attribute being set.

These flags are only supported by guest_memfd, hence, if
vm_memory_attributes is enabled, return 0 - no flags are supported when
KVM_SET_MEMORY_ATTRIBUTES2 is sent to a VM fd.

Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
Documentation/virt/kvm/api.rst | 3 +++
arch/x86/kvm/x86.c | 5 +++--
include/linux/kvm_host.h | 11 ++++++++++-
include/uapi/linux/kvm.h | 1 +
virt/kvm/guest_memfd.c | 6 ++++--
virt/kvm/kvm_main.c | 5 +++++
6 files changed, 26 insertions(+), 5 deletions(-)

diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index aaa4a82f0b75d..38938243c2dfd 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -6685,6 +6685,9 @@ guarantees are made on offset ranges that do not have memory allocated
and range [0x0000, 0x3000) was set to shared, the content mode would
apply to only to offset ranges [0x0000, 0x1000) and [0x2000, 0x3000).

+The supported content modes can be queried using
+``KVM_CAP_MEMORY_ATTRIBUTES2_FLAGS``.
+
See also: :ref: `KVM_SET_MEMORY_ATTRIBUTES`.

.. _kvm_run:
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 296ed3b8ace6c..92709735613d5 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -14195,7 +14195,8 @@ void kvm_arch_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end)
}
#endif

-u64 kvm_arch_gmem_supported_content_modes(struct kvm *kvm, bool to_private)
+u64 kvm_arch_gmem_supported_content_modes(struct kvm *kvm, bool to_private,
+ bool for_cap)
{
if (!kvm) {
return KVM_SET_MEMORY_ATTRIBUTES2_ZERO |
@@ -14227,7 +14228,7 @@ u64 kvm_arch_gmem_supported_content_modes(struct kvm *kvm, bool to_private)
* shared, memory contents for pages that had already
* been faulted could be zeroed.
*/
- if (to_private && !kvm->arch.pre_fault_allowed)
+ if (for_cap || (to_private && !kvm->arch.pre_fault_allowed))
supported |= KVM_SET_MEMORY_ATTRIBUTES2_PRESERVE;

return supported;
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 458bad0083c37..13d126dde32f1 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -742,7 +742,8 @@ static inline u64 kvm_gmem_get_supported_flags(struct kvm *kvm)
return flags;
}

-u64 kvm_arch_gmem_supported_content_modes(struct kvm *kvm, bool to_private);
+u64 kvm_arch_gmem_supported_content_modes(struct kvm *kvm, bool to_private,
+ bool for_cap);
int kvm_gmem_apply_content_mode_zero(struct inode *inode, pgoff_t start,
pgoff_t end);
int kvm_arch_gmem_apply_content_mode_zero(struct kvm *kvm, struct inode *inode,
@@ -2551,6 +2552,14 @@ static inline u64 kvm_supported_mem_attributes(struct kvm *kvm)
return 0;
}

+static inline u64 kvm_supported_set_mem_attributes2_flags(struct kvm *kvm)
+{
+ if (!IS_ENABLED(CONFIG_KVM_GUEST_MEMFD))
+ return 0;
+
+ return kvm_arch_gmem_supported_content_modes(kvm, false, true);
+}
+
typedef unsigned long (kvm_get_memory_attributes_t)(struct kvm *kvm, gfn_t gfn);
DECLARE_STATIC_CALL(__kvm_get_memory_attributes, kvm_get_memory_attributes_t);

diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index c7cc6c22c2023..c0d465a5577da 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -997,6 +997,7 @@ struct kvm_enable_cap {
#define KVM_CAP_S390_KEYOP 247
#define KVM_CAP_S390_VSIE_ESAMODE 248
#define KVM_CAP_GUEST_MEMFD_MEMORY_ATTRIBUTES 249
+#define KVM_CAP_MEMORY_ATTRIBUTES2_FLAGS 250

struct kvm_irq_routing_irqchip {
__u32 irqchip;
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index 5c1db67e6fd35..071bf636ba5c0 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -693,7 +693,8 @@ static void kvm_gmem_invalidate(struct inode *inode, pgoff_t start, pgoff_t end)
static void kvm_gmem_invalidate(struct inode *inode, pgoff_t start, pgoff_t end) {}
#endif

-u64 __weak kvm_arch_gmem_supported_content_modes(struct kvm *kvm, bool to_private)
+u64 __weak kvm_arch_gmem_supported_content_modes(struct kvm *kvm,
+ bool to_private, bool for_cap)
{
/* Architectures must override with supported modes. */
return 0;
@@ -709,7 +710,8 @@ static bool kvm_gmem_content_mode_is_supported(struct kvm *kvm,
if (content_mode == KVM_SET_MEMORY_ATTRIBUTES2_ZERO && to_private)
return false;

- return kvm_arch_gmem_supported_content_modes(kvm, to_private) & content_mode;
+ return kvm_arch_gmem_supported_content_modes(kvm, to_private, false) &
+ content_mode;
}

int kvm_gmem_apply_content_mode_zero(struct inode *inode, pgoff_t start,
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 3bf212fd99193..9fa6ecebab939 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -4979,6 +4979,11 @@ static int kvm_vm_ioctl_check_extension_generic(struct kvm *kvm, long arg)
return 0;

return kvm_supported_mem_attributes(kvm);
+ case KVM_CAP_MEMORY_ATTRIBUTES2_FLAGS:
+ if (vm_memory_attributes)
+ return 0;
+
+ return kvm_supported_set_mem_attributes2_flags(kvm);
#endif
default:
break;

--
2.54.0.545.g6539524ca2-goog