* [PATCH v4 01/11] liveupdate: Add LIVEUPDATE_GUEST_MEMFD config option
2026-07-28 12:11 [PATCH v4 00/11] liveupdate: kvm: Guest_memfd preservation Tarun Sahu
@ 2026-07-28 12:11 ` Tarun Sahu
2026-07-28 12:22 ` sashiko-bot
2026-07-28 12:11 ` [PATCH v4 02/11] KVM: Introduce kvm_create_vm_file() helper Tarun Sahu
` (9 subsequent siblings)
10 siblings, 1 reply; 18+ messages in thread
From: Tarun Sahu @ 2026-07-28 12:11 UTC (permalink / raw)
To: ackerleytng, fuad.tabba, Andrew Morton, seanjc, dmatlack,
Shuah Khan, Jonathan Corbet, david, Tarun Sahu, Pasha Tatashin,
Pratyush Yadav, sagis, Paolo Bonzini, Mike Rapoport,
Alexander Graf
Cc: linux-kselftest, andre.przywara, michael.roth, linux-kernel,
linux-mm, will, vannapurve, maz, fvdl, kvm, oliver.upton, kvmarm,
alexandru.elisei, skhawaja, aneesh.kumar, linux-doc,
David Hildenbrand, yan.y.zhao, kexec, suzuki.poulose
Introduce the LIVEUPDATE_GUEST_MEMFD Kconfig option. This option
enables live update support for KVM guest_memfd files, enabling
guest_memfd-backed memory preservation across kernel upgrades.
Currently this support only guest_memfd files that are full-shared
(INIT_SHARED and no support for private_mem).
Signed-off-by: Tarun Sahu <tarunsahu@google.com>
---
kernel/liveupdate/Kconfig | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/kernel/liveupdate/Kconfig b/kernel/liveupdate/Kconfig
index c13af38ba23a..2490f9a9b838 100644
--- a/kernel/liveupdate/Kconfig
+++ b/kernel/liveupdate/Kconfig
@@ -86,4 +86,19 @@ config LIVEUPDATE_MEMFD
If unsure, say N.
+config LIVEUPDATE_GUEST_MEMFD
+ bool "Live update support for guest_memfd"
+ depends on LIVEUPDATE
+ depends on KVM_GUEST_MEMFD
+ default LIVEUPDATE
+ help
+ Enable live update support for KVM guest_memfd files. This allows
+ preserving VM Memory backed by guest_memfd file across kernel live
+ updates.
+
+ This can only be used for the guest_memfd that are fully-shared
+ and pre-faulted.
+
+ If unsure, say N.
+
endmenu
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v4 01/11] liveupdate: Add LIVEUPDATE_GUEST_MEMFD config option
2026-07-28 12:11 ` [PATCH v4 01/11] liveupdate: Add LIVEUPDATE_GUEST_MEMFD config option Tarun Sahu
@ 2026-07-28 12:22 ` sashiko-bot
0 siblings, 0 replies; 18+ messages in thread
From: sashiko-bot @ 2026-07-28 12:22 UTC (permalink / raw)
To: Tarun Sahu; +Cc: kvm, Marc Zyngier, kvmarm, Oliver Upton
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The patch introduces the `LIVEUPDATE_GUEST_MEMFD` Kconfig option claiming to enable a new feature, but there is no actual implementation provided.
--
--- Patch [1]: [PATCH v4 01/11] liveupdate: Add LIVEUPDATE_GUEST_MEMFD config option ---
commit e0c3fde657d56c548cd9a7467d2143f0f42c5242
Author: Tarun Sahu <tarunsahu@google.com>
liveupdate: Add LIVEUPDATE_GUEST_MEMFD config option
This commit introduces the LIVEUPDATE_GUEST_MEMFD Kconfig option, which aims
to enable live update support for KVM guest_memfd files.
> diff --git a/kernel/liveupdate/Kconfig b/kernel/liveupdate/Kconfig
> index 1a8513f16ef7c..0bbc4037192e9 100644
> --- a/kernel/liveupdate/Kconfig
> +++ b/kernel/liveupdate/Kconfig
> @@ -88,4 +88,19 @@ config LIVEUPDATE_MEMFD
>
> If unsure, say N.
>
> +config LIVEUPDATE_GUEST_MEMFD
> + bool "Live update support for guest_memfd"
> + depends on LIVEUPDATE
> + depends on KVM_GUEST_MEMFD
> + default LIVEUPDATE
[Severity: Medium]
Is the implementation for this feature missing from this patch?
The commit message notes that this patch enables live update support for KVM
guest_memfd files, but the newly added LIVEUPDATE_GUEST_MEMFD symbol appears
to be unreferenced in the tree. Since there are no Makefiles, C files, or
preprocessor checks that use it, this currently acts as dead code.
Could the implementation be missing, or was this intended to be introduced as
part of a larger series where the symbol is used in a subsequent patch?
> + help
> + Enable live update support for KVM guest_memfd files. This allows
> + preserving VM Memory backed by guest_memfd file across kernel live
> + updates.
> +
> + This can only be used for the guest_memfd that are fully-shared
> + and pre-faulted.
> +
> + If unsure, say N.
> +
> endmenu
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260728121138.1103610-1-tarunsahu@google.com?part=1
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v4 02/11] KVM: Introduce kvm_create_vm_file() helper
2026-07-28 12:11 [PATCH v4 00/11] liveupdate: kvm: Guest_memfd preservation Tarun Sahu
2026-07-28 12:11 ` [PATCH v4 01/11] liveupdate: Add LIVEUPDATE_GUEST_MEMFD config option Tarun Sahu
@ 2026-07-28 12:11 ` Tarun Sahu
2026-07-28 12:11 ` [PATCH v4 03/11] KVM: Export kvm_uevent_notify_vm_create() Tarun Sahu
` (8 subsequent siblings)
10 siblings, 0 replies; 18+ messages in thread
From: Tarun Sahu @ 2026-07-28 12:11 UTC (permalink / raw)
To: ackerleytng, fuad.tabba, Andrew Morton, seanjc, dmatlack,
Shuah Khan, Jonathan Corbet, david, Tarun Sahu, Pasha Tatashin,
Pratyush Yadav, sagis, Paolo Bonzini, Mike Rapoport,
Alexander Graf
Cc: linux-kselftest, andre.przywara, michael.roth, linux-kernel,
linux-mm, will, vannapurve, maz, fvdl, kvm, oliver.upton, kvmarm,
alexandru.elisei, skhawaja, aneesh.kumar, linux-doc,
David Hildenbrand, yan.y.zhao, kexec, suzuki.poulose
- Move ITOA_MAX_LEN to kvm_mm.h for reuse by upcoming kvm_luo code.
- Introduce kvm_create_vm_file() to wrap kvm_create_vm() and
anon_inode_getfile(), providing a unified VM file creation API.
Signed-off-by: Tarun Sahu <tarunsahu@google.com>
---
include/linux/kvm_host.h | 1 +
virt/kvm/kvm_main.c | 48 ++++++++++++++++++++++------------------
virt/kvm/kvm_mm.h | 3 +++
3 files changed, 31 insertions(+), 21 deletions(-)
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index ab8cfaec82d3..79b084887cdb 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -1074,6 +1074,7 @@ void kvm_get_kvm(struct kvm *kvm);
bool kvm_get_kvm_safe(struct kvm *kvm);
void kvm_put_kvm(struct kvm *kvm);
bool file_is_kvm(struct file *file);
+struct file *kvm_create_vm_file(unsigned long type, const char *fdname);
void kvm_put_kvm_no_destroy(struct kvm *kvm);
static inline struct kvm_memslots *__kvm_memslots(struct kvm *kvm, int as_id)
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index e44c20c04961..3339b8fcd59f 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -67,9 +67,6 @@
#include <linux/kvm_dirty_ring.h>
-/* Worst case buffer size needed for holding an integer. */
-#define ITOA_MAX_LEN 12
-
MODULE_AUTHOR("Qumranet");
MODULE_DESCRIPTION("Kernel-based Virtual Machine (KVM) Hypervisor");
MODULE_LICENSE("GPL");
@@ -5477,11 +5474,34 @@ bool file_is_kvm(struct file *file)
}
EXPORT_SYMBOL_FOR_KVM_INTERNAL(file_is_kvm);
+struct file *kvm_create_vm_file(unsigned long type, const char *fdname)
+{
+ struct kvm *kvm = kvm_create_vm(type, fdname);
+ struct file *file;
+
+ if (IS_ERR(kvm))
+ return ERR_CAST(kvm);
+
+ file = anon_inode_getfile("kvm-vm", &kvm_vm_fops, kvm, O_RDWR);
+ if (IS_ERR(file)) {
+ kvm_put_kvm(kvm);
+ return file;
+ }
+
+ /*
+ * Don't call kvm_put_kvm anymore at this point; file->f_op is
+ * already set, with ->release() being kvm_vm_release(). In error
+ * cases it will be called by the final fput(file) and will take
+ * care of doing kvm_put_kvm(kvm).
+ */
+
+ return file;
+}
+
static int kvm_dev_ioctl_create_vm(unsigned long type)
{
char fdname[ITOA_MAX_LEN + 1];
int r, fd;
- struct kvm *kvm;
struct file *file;
fd = get_unused_fd_flags(O_CLOEXEC);
@@ -5490,31 +5510,17 @@ static int kvm_dev_ioctl_create_vm(unsigned long type)
snprintf(fdname, sizeof(fdname), "%d", fd);
- kvm = kvm_create_vm(type, fdname);
- if (IS_ERR(kvm)) {
- r = PTR_ERR(kvm);
- goto put_fd;
- }
-
- file = anon_inode_getfile("kvm-vm", &kvm_vm_fops, kvm, O_RDWR);
+ file = kvm_create_vm_file(type, fdname);
if (IS_ERR(file)) {
r = PTR_ERR(file);
- goto put_kvm;
+ goto put_fd;
}
- /*
- * Don't call kvm_put_kvm anymore at this point; file->f_op is
- * already set, with ->release() being kvm_vm_release(). In error
- * cases it will be called by the final fput(file) and will take
- * care of doing kvm_put_kvm(kvm).
- */
- kvm_uevent_notify_change(KVM_EVENT_CREATE_VM, kvm);
+ kvm_uevent_notify_change(KVM_EVENT_CREATE_VM, file->private_data);
fd_install(fd, file);
return fd;
-put_kvm:
- kvm_put_kvm(kvm);
put_fd:
put_unused_fd(fd);
return r;
diff --git a/virt/kvm/kvm_mm.h b/virt/kvm/kvm_mm.h
index 7510ca915dd1..624161793fec 100644
--- a/virt/kvm/kvm_mm.h
+++ b/virt/kvm/kvm_mm.h
@@ -6,6 +6,9 @@
#include <linux/kvm.h>
#include <linux/kvm_types.h>
+/* Worst case buffer size needed for holding an integer as a string. */
+#define ITOA_MAX_LEN 12
+
/*
* Architectures can choose whether to use an rwlock or spinlock
* for the mmu_lock. These macros, for use in common code
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v4 03/11] KVM: Export kvm_uevent_notify_vm_create()
2026-07-28 12:11 [PATCH v4 00/11] liveupdate: kvm: Guest_memfd preservation Tarun Sahu
2026-07-28 12:11 ` [PATCH v4 01/11] liveupdate: Add LIVEUPDATE_GUEST_MEMFD config option Tarun Sahu
2026-07-28 12:11 ` [PATCH v4 02/11] KVM: Introduce kvm_create_vm_file() helper Tarun Sahu
@ 2026-07-28 12:11 ` Tarun Sahu
2026-07-28 12:26 ` sashiko-bot
2026-07-28 12:11 ` [PATCH v4 04/11] KVM: Track weak reference to vm_file in struct kvm Tarun Sahu
` (7 subsequent siblings)
10 siblings, 1 reply; 18+ messages in thread
From: Tarun Sahu @ 2026-07-28 12:11 UTC (permalink / raw)
To: ackerleytng, fuad.tabba, Andrew Morton, seanjc, dmatlack,
Shuah Khan, Jonathan Corbet, david, Tarun Sahu, Pasha Tatashin,
Pratyush Yadav, sagis, Paolo Bonzini, Mike Rapoport,
Alexander Graf
Cc: linux-kselftest, andre.przywara, michael.roth, linux-kernel,
linux-mm, will, vannapurve, maz, fvdl, kvm, oliver.upton, kvmarm,
alexandru.elisei, skhawaja, aneesh.kumar, linux-doc,
David Hildenbrand, yan.y.zhao, kexec, suzuki.poulose
- Export kvm_uevent_notify_vm_create() to allow triggering KVM event
notifications during VM creation.
Signed-off-by: Tarun Sahu <tarunsahu@google.com>
---
include/linux/kvm_host.h | 1 +
virt/kvm/kvm_main.c | 5 +++++
2 files changed, 6 insertions(+)
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 79b084887cdb..d9908c19e42f 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -1076,6 +1076,7 @@ void kvm_put_kvm(struct kvm *kvm);
bool file_is_kvm(struct file *file);
struct file *kvm_create_vm_file(unsigned long type, const char *fdname);
void kvm_put_kvm_no_destroy(struct kvm *kvm);
+void kvm_uevent_notify_vm_create(struct kvm *kvm);
static inline struct kvm_memslots *__kvm_memslots(struct kvm *kvm, int as_id)
{
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 3339b8fcd59f..701d4596a91c 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -6348,6 +6348,11 @@ static void kvm_uevent_notify_change(unsigned int type, struct kvm *kvm)
kfree(env);
}
+void kvm_uevent_notify_vm_create(struct kvm *kvm)
+{
+ kvm_uevent_notify_change(KVM_EVENT_CREATE_VM, kvm);
+}
+
static void kvm_init_debug(void)
{
const struct file_operations *fops;
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v4 03/11] KVM: Export kvm_uevent_notify_vm_create()
2026-07-28 12:11 ` [PATCH v4 03/11] KVM: Export kvm_uevent_notify_vm_create() Tarun Sahu
@ 2026-07-28 12:26 ` sashiko-bot
0 siblings, 0 replies; 18+ messages in thread
From: sashiko-bot @ 2026-07-28 12:26 UTC (permalink / raw)
To: Tarun Sahu; +Cc: kvm, kvmarm, Oliver Upton, Marc Zyngier
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The patch claims to export `kvm_uevent_notify_vm_create()` but fails to include the `EXPORT_SYMBOL_GPL()` or `EXPORT_SYMBOL_FOR_KVM_INTERNAL()` macro.
--
--- Patch [3]: [PATCH v4 03/11] KVM: Export kvm_uevent_notify_vm_create() ---
commit 5e642ea16f0d267e622a352de60bfe397ecf79df
Author: Tarun Sahu <tarunsahu@google.com>
KVM: Export kvm_uevent_notify_vm_create()
This commit exposes the kvm_uevent_notify_vm_create function to allow
triggering KVM event notifications during VM creation.
[ ... ]
> diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
> --- a/virt/kvm/kvm_main.c
> +++ b/virt/kvm/kvm_main.c
> @@ -6348,6 +6348,11 @@ static void kvm_uevent_notify_change(unsigned int type, struct kvm *kvm)
> kfree(env);
> }
>
> +void kvm_uevent_notify_vm_create(struct kvm *kvm)
> +{
> + kvm_uevent_notify_change(KVM_EVENT_CREATE_VM, kvm);
> +}
[Severity: Low]
The commit message mentions exporting this function, but is it missing an
EXPORT_SYMBOL_GPL or EXPORT_SYMBOL_FOR_KVM_INTERNAL declaration?
Without the macro, won't dynamically loaded architecture-specific modules
like kvm-intel.ko hit undefined symbol linking errors when they try to
call it?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260728121138.1103610-1-tarunsahu@google.com?part=3
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v4 04/11] KVM: Track weak reference to vm_file in struct kvm
2026-07-28 12:11 [PATCH v4 00/11] liveupdate: kvm: Guest_memfd preservation Tarun Sahu
` (2 preceding siblings ...)
2026-07-28 12:11 ` [PATCH v4 03/11] KVM: Export kvm_uevent_notify_vm_create() Tarun Sahu
@ 2026-07-28 12:11 ` Tarun Sahu
2026-07-28 12:11 ` [PATCH v4 05/11] KVM: LUO: Support VM preservation across live updates Tarun Sahu
` (6 subsequent siblings)
10 siblings, 0 replies; 18+ messages in thread
From: Tarun Sahu @ 2026-07-28 12:11 UTC (permalink / raw)
To: ackerleytng, fuad.tabba, Andrew Morton, seanjc, dmatlack,
Shuah Khan, Jonathan Corbet, david, Tarun Sahu, Pasha Tatashin,
Pratyush Yadav, sagis, Paolo Bonzini, Mike Rapoport,
Alexander Graf
Cc: linux-kselftest, andre.przywara, michael.roth, linux-kernel,
linux-mm, will, vannapurve, maz, fvdl, kvm, oliver.upton, kvmarm,
alexandru.elisei, skhawaja, aneesh.kumar, linux-doc,
David Hildenbrand, yan.y.zhao, kexec, suzuki.poulose
- Add a weak reference 'vm_file' in struct kvm under
CONFIG_LIVEUPDATE_GUEST_MEMFD to enable reverse file resolution
without circular lifetime dependencies.
Signed-off-by: Tarun Sahu <tarunsahu@google.com>
---
include/linux/kvm_host.h | 12 ++++++++++++
virt/kvm/kvm_main.c | 26 ++++++++++++++++++++++++++
2 files changed, 38 insertions(+)
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index d9908c19e42f..8da9589425ee 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -874,6 +874,18 @@ struct kvm {
#ifdef CONFIG_KVM_GENERIC_MEMORY_ATTRIBUTES
/* Protected by slots_lock (for writes) and RCU (for reads) */
struct xarray mem_attr_array;
+#endif
+#ifdef CONFIG_LIVEUPDATE_GUEST_MEMFD
+ /*
+ * Weak reference to the VFS file backing this KVM instance. Stored
+ * without incrementing the file refcount to prevent a circular lifetime
+ * dependency (since file->private_data already pins this struct kvm).
+ * Used exclusively to resolve the file pointer back from struct kvm.
+ *
+ * Written/cleared via rcu_assign_pointer() and read locklessly under
+ * RCU (e.g. via get_file_active() to prevent ABA races).
+ */
+ struct file __rcu *vm_file;
#endif
char stats_id[KVM_STATS_NAME_SIZE];
};
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 701d4596a91c..14c32541ae34 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -1346,6 +1346,19 @@ static int kvm_vm_release(struct inode *inode, struct file *filp)
{
struct kvm *kvm = filp->private_data;
+#ifdef CONFIG_LIVEUPDATE_GUEST_MEMFD
+ /*
+ * Clear the weak reference of the vm file.
+ * In case vm file is closed by userspace, but kvm still has
+ * other users like vCPUs, clearing this pointer ensures
+ * that we don't have a dangling pointer to a closed file.
+ *
+ * Cleared via rcu_assign_pointer() to ensure proper memory visibility
+ * for concurrent lockless readers under RCU.
+ */
+ rcu_assign_pointer(kvm->vm_file, NULL);
+#endif
+
kvm_irqfd_release(kvm);
kvm_put_kvm(kvm);
@@ -5488,6 +5501,19 @@ struct file *kvm_create_vm_file(unsigned long type, const char *fdname)
return file;
}
+#ifdef CONFIG_LIVEUPDATE_GUEST_MEMFD
+ /*
+ * Weak reference to the file (without get_file()) to prevent a circular
+ * dependency. Safe because the file's release path clears this pointer
+ * and drops its reference to the VM.
+ *
+ * Written via rcu_assign_pointer() because the pointer can be read
+ * locklessly under RCU (e.g., in kvm_gmem_luo_preserve() via
+ * get_file_active() to prevent lockless ABA races).
+ */
+ rcu_assign_pointer(kvm->vm_file, file);
+#endif
+
/*
* Don't call kvm_put_kvm anymore at this point; file->f_op is
* already set, with ->release() being kvm_vm_release(). In error
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v4 05/11] KVM: LUO: Support VM preservation across live updates
2026-07-28 12:11 [PATCH v4 00/11] liveupdate: kvm: Guest_memfd preservation Tarun Sahu
` (3 preceding siblings ...)
2026-07-28 12:11 ` [PATCH v4 04/11] KVM: Track weak reference to vm_file in struct kvm Tarun Sahu
@ 2026-07-28 12:11 ` Tarun Sahu
2026-07-28 12:27 ` sashiko-bot
2026-07-28 12:11 ` [PATCH v4 06/11] KVM: guest_memfd: Move internal definitions to internal header Tarun Sahu
` (5 subsequent siblings)
10 siblings, 1 reply; 18+ messages in thread
From: Tarun Sahu @ 2026-07-28 12:11 UTC (permalink / raw)
To: ackerleytng, fuad.tabba, Andrew Morton, seanjc, dmatlack,
Shuah Khan, Jonathan Corbet, david, Tarun Sahu, Pasha Tatashin,
Pratyush Yadav, sagis, Paolo Bonzini, Mike Rapoport,
Alexander Graf
Cc: linux-kselftest, andre.przywara, michael.roth, linux-kernel,
linux-mm, will, vannapurve, maz, fvdl, kvm, oliver.upton, kvmarm,
alexandru.elisei, skhawaja, aneesh.kumar, linux-doc,
David Hildenbrand, yan.y.zhao, kexec, suzuki.poulose
Register a Live Update Orchestrator (LUO) file handler for KVM VM files
to serialize and deserialize VM state across kexec live updates.
Currently, Only VM type (e.g. arch.vm_type on x86) is preserved as part
of VM preservation. On retrieval, kvm_luo_retrieve() recreates the KVM VM file
via kvm_create_vm_file() and use an atomically incremented ID for the internal
fdname, as the final fdname assigned by userspace is not yet known during
retrieval. As this fdname is only used in debugfs infra, This will not
break any UAPI.
This infrastructure establishes the foundation for preserving guest_memfd
instances across live updates, and can be expanded in the future to
preserve additional VM state.
Also updates MAINTAINERS to include virt/kvm/kvm_luo.c and
include/linux/kho/abi/kvm.h.
Signed-off-by: Tarun Sahu <tarunsahu@google.com>
---
MAINTAINERS | 11 ++
include/linux/kho/abi/kvm.h | 39 ++++++++
virt/kvm/Makefile.kvm | 1 +
virt/kvm/kvm_luo.c | 195 ++++++++++++++++++++++++++++++++++++
virt/kvm/kvm_main.c | 8 ++
virt/kvm/kvm_mm.h | 8 ++
6 files changed, 262 insertions(+)
create mode 100644 include/linux/kho/abi/kvm.h
create mode 100644 virt/kvm/kvm_luo.c
diff --git a/MAINTAINERS b/MAINTAINERS
index a3ed337e827d..0283f0fd6ef4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14539,6 +14539,17 @@ S: Maintained
F: Documentation/devicetree/bindings/leds/backlight/kinetic,ktz8866.yaml
F: drivers/video/backlight/ktz8866.c
+KVM LIVE UPDATE
+M: Pasha Tatashin <pasha.tatashin@soleen.com>
+M: Mike Rapoport <rppt@kernel.org>
+M: Pratyush Yadav <pratyush@kernel.org>
+R: Tarun Sahu <tarunsahu@google.com>
+L: kexec@lists.infradead.org
+L: kvm@vger.kernel.org
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux.git
+F: virt/kvm/kvm_luo.c
+
KVM PARAVIRT (KVM/paravirt)
M: Paolo Bonzini <pbonzini@redhat.com>
R: Vitaly Kuznetsov <vkuznets@redhat.com>
diff --git a/include/linux/kho/abi/kvm.h b/include/linux/kho/abi/kvm.h
new file mode 100644
index 000000000000..718db68a541a
--- /dev/null
+++ b/include/linux/kho/abi/kvm.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2026, Google LLC.
+ * Tarun Sahu <tarunsahu@google.com>
+ *
+ * KVM Preservation ABI for Live Update Orchestrator (LUO)
+ */
+#ifndef _LINUX_KHO_ABI_KVM_H
+#define _LINUX_KHO_ABI_KVM_H
+
+#include <linux/types.h>
+#include <linux/kho/abi/kexec_handover.h>
+
+/**
+ * DOC: KVM Live Update ABI
+ *
+ * KVM uses the ABI defined below for preserving its state
+ * across a kexec reboot using the LUO.
+ *
+ * The state is serialized into a packed structure `struct kvm_luo_ser`
+ * which is handed over to the next kernel via the KHO mechanism.
+ *
+ * This interface is a contract. Any modification to the structure layout
+ * constitutes a breaking change. Such changes require incrementing the
+ * version number in the KVM_LUO_FH_COMPATIBLE compatibility string.
+ */
+
+/**
+ * struct kvm_luo_ser - Main serialization structure for a KVM VM.
+ * @type: The type of VM.
+ */
+struct kvm_luo_ser {
+ u64 type;
+} __packed;
+
+/* The compatibility string for KVM VM file handler */
+#define KVM_LUO_FH_COMPATIBLE "kvm_vm_luo_v1"
+
+#endif /* _LINUX_KHO_ABI_KVM_H */
diff --git a/virt/kvm/Makefile.kvm b/virt/kvm/Makefile.kvm
index d047d4cf58c9..c1a962159264 100644
--- a/virt/kvm/Makefile.kvm
+++ b/virt/kvm/Makefile.kvm
@@ -13,3 +13,4 @@ kvm-$(CONFIG_HAVE_KVM_IRQ_ROUTING) += $(KVM)/irqchip.o
kvm-$(CONFIG_HAVE_KVM_DIRTY_RING) += $(KVM)/dirty_ring.o
kvm-$(CONFIG_HAVE_KVM_PFNCACHE) += $(KVM)/pfncache.o
kvm-$(CONFIG_KVM_GUEST_MEMFD) += $(KVM)/guest_memfd.o
+kvm-$(CONFIG_LIVEUPDATE_GUEST_MEMFD) += $(KVM)/kvm_luo.o
diff --git a/virt/kvm/kvm_luo.c b/virt/kvm/kvm_luo.c
new file mode 100644
index 000000000000..025b53151b6a
--- /dev/null
+++ b/virt/kvm/kvm_luo.c
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (c) 2026, Google LLC.
+ * Tarun Sahu <tarunsahu@google.com>
+ *
+ * KVM VM Preservation for Live Update Orchestrator (LUO)
+ */
+
+/**
+ * DOC: KVM VM Preservation via LUO
+ *
+ * Overview
+ * ========
+ *
+ * KVM virtual machines (VMs) can be preserved over a kexec reboot using the
+ * Live Update Orchestrator (LUO) file preservation. This allows userspace
+ * to preserve KVM VM state across kexec reboots.
+ *
+ * The preservation is not intended to be fully transparent. Only specific
+ * VM configuration and state are preserved, while other aspects of the VM
+ * must be re-established or re-configured by userspace after retrieval.
+ *
+ * Preserved Properties
+ * ====================
+ *
+ * The following properties of the KVM VM are preserved across kexec:
+ *
+ * VM Type
+ * The VM type (e.g., on x86 architecture, the vm_type parameter) is
+ * preserved.
+ *
+ * Non-Preserved Properties
+ * ========================
+ *
+ * The preservation does not cover:
+ *
+ * - vCPUs and vCPU states
+ * - Memspots / Memory slot layout (memslots)
+ * - Interrupt controllers and IRQ routings
+ * - Coalesced MMIO zones
+ * - Device bindings (VFIO/Eventfds)
+ * - Active paging or guest registers state
+ * - etc
+ */
+#include <linux/liveupdate.h>
+#include <linux/kvm_host.h>
+#include <linux/pagemap.h>
+#include <linux/file.h>
+#include <linux/err.h>
+#include <linux/anon_inodes.h>
+#include <linux/magic.h>
+#include <linux/kexec_handover.h>
+#include <linux/kho/abi/kexec_handover.h>
+#include <linux/kho/abi/kvm.h>
+#include "kvm_mm.h"
+
+static bool kvm_luo_can_preserve(struct liveupdate_file_handler *handler,
+ struct file *file)
+{
+ return file_is_kvm(file);
+}
+
+static int kvm_luo_preserve(struct liveupdate_file_op_args *args)
+{
+ struct kvm *kvm = args->file->private_data;
+ struct kvm_luo_ser *ser;
+
+ if (kvm->vm_dead || kvm->vm_bugged)
+ return -EINVAL;
+
+ ser = kho_alloc_preserve(sizeof(*ser));
+ if (IS_ERR(ser))
+ return PTR_ERR(ser);
+
+#if defined(CONFIG_X86)
+ ser->type = kvm->arch.vm_type;
+#elif defined(CONFIG_ARM64)
+ ser->type = kvm_phys_shift(&kvm->arch.mmu);
+ if (kvm_vm_is_protected(kvm))
+ ser->type |= KVM_VM_TYPE_ARM_PROTECTED;
+
+#else
+ ser->type = 0;
+#endif
+
+ args->serialized_data = virt_to_phys(ser);
+ return 0;
+}
+
+static atomic_t restored_vm_id = ATOMIC_INIT(0);
+
+static int kvm_luo_retrieve(struct liveupdate_file_op_args *args)
+{
+ char fdname[ITOA_MAX_LEN + 1];
+ struct kvm_luo_ser *ser;
+ struct file *file;
+ struct kvm *kvm;
+ int err = 0;
+
+ if (!args->serialized_data)
+ return -EINVAL;
+
+ ser = phys_to_virt(args->serialized_data);
+
+ snprintf(fdname, sizeof(fdname), "%d",
+ atomic_inc_return(&restored_vm_id));
+
+ file = kvm_create_vm_file(ser->type, fdname);
+ if (IS_ERR(file)) {
+ err = PTR_ERR(file);
+ goto err_free_ser;
+ }
+
+ kvm = file->private_data;
+
+ args->file = file;
+ kho_restore_free(ser);
+
+ kvm_uevent_notify_vm_create(kvm);
+ return 0;
+
+err_free_ser:
+ kho_restore_free(ser);
+ return err;
+}
+
+static void kvm_luo_unpreserve(struct liveupdate_file_op_args *args)
+{
+ struct kvm_luo_ser *ser;
+
+ /*
+ * in case preservation failed, args->serialized_data will
+ * be NULL and kvm_luo_preserve takes care of cleaning up.
+ * If preserve succeeds, this condition fails and unpreserve
+ * function takes care of cleaning up.
+ */
+ if (WARN_ON_ONCE(!args->serialized_data))
+ return;
+
+ ser = phys_to_virt(args->serialized_data);
+
+ kho_unpreserve_free(ser);
+}
+
+static void kvm_luo_finish(struct liveupdate_file_op_args *args)
+{
+ struct kvm_luo_ser *ser;
+
+ /*
+ * If retrieve_status is true or set to error, nothing to do here.
+ * Already cleaned up in kvm_luo_retrieve().
+ */
+ if (args->retrieve_status)
+ return;
+
+ if (!args->serialized_data)
+ return;
+
+ ser = phys_to_virt(args->serialized_data);
+
+ kho_restore_free(ser);
+}
+
+static const struct liveupdate_file_ops kvm_luo_file_ops = {
+ .can_preserve = kvm_luo_can_preserve,
+ .preserve = kvm_luo_preserve,
+ .retrieve = kvm_luo_retrieve,
+ .unpreserve = kvm_luo_unpreserve,
+ .finish = kvm_luo_finish,
+ .owner = THIS_MODULE,
+};
+
+static struct liveupdate_file_handler kvm_luo_handler = {
+ .ops = &kvm_luo_file_ops,
+ .compatible = KVM_LUO_FH_COMPATIBLE,
+};
+
+int kvm_luo_init(void)
+{
+ int err = liveupdate_register_file_handler(&kvm_luo_handler);
+
+ if (err && err != -EOPNOTSUPP) {
+ pr_err("Could not register kvm_vm_luo handler: %pe\n", ERR_PTR(err));
+ return err;
+ }
+
+ return 0;
+}
+
+void kvm_luo_exit(void)
+{
+ liveupdate_unregister_file_handler(&kvm_luo_handler);
+}
+
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 14c32541ae34..d9c3dd1afb05 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -6577,6 +6577,10 @@ int kvm_init(unsigned vcpu_size, unsigned vcpu_align, struct module *module)
if (r)
goto err_virt;
+ r = kvm_luo_init();
+ if (r)
+ goto err_luo;
+
/*
* Registration _must_ be the very last thing done, as this exposes
* /dev/kvm to userspace, i.e. all infrastructure must be setup!
@@ -6590,6 +6594,8 @@ int kvm_init(unsigned vcpu_size, unsigned vcpu_align, struct module *module)
return 0;
err_register:
+ kvm_luo_exit();
+err_luo:
kvm_uninit_virtualization();
err_virt:
kvm_gmem_exit();
@@ -6619,6 +6625,8 @@ void kvm_exit(void)
*/
misc_deregister(&kvm_dev);
+ kvm_luo_exit();
+
kvm_uninit_virtualization();
debugfs_remove_recursive(kvm_debugfs_dir);
diff --git a/virt/kvm/kvm_mm.h b/virt/kvm/kvm_mm.h
index 624161793fec..87198715fb01 100644
--- a/virt/kvm/kvm_mm.h
+++ b/virt/kvm/kvm_mm.h
@@ -100,4 +100,12 @@ static inline void kvm_gmem_unbind(struct kvm_memory_slot *slot)
}
#endif /* CONFIG_KVM_GUEST_MEMFD */
+#ifdef CONFIG_LIVEUPDATE_GUEST_MEMFD
+int kvm_luo_init(void);
+void kvm_luo_exit(void);
+#else
+static inline int kvm_luo_init(void) { return 0; }
+static inline void kvm_luo_exit(void) {}
+#endif /* CONFIG_LIVEUPDATE_GUEST_MEMFD */
+
#endif /* __KVM_MM_H__ */
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v4 05/11] KVM: LUO: Support VM preservation across live updates
2026-07-28 12:11 ` [PATCH v4 05/11] KVM: LUO: Support VM preservation across live updates Tarun Sahu
@ 2026-07-28 12:27 ` sashiko-bot
0 siblings, 0 replies; 18+ messages in thread
From: sashiko-bot @ 2026-07-28 12:27 UTC (permalink / raw)
To: Tarun Sahu; +Cc: Marc Zyngier, kvm, kvmarm, Oliver Upton
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] The generated `fdname` for a restored VM can collide with the file descriptor of a newly created VM in the same process, causing `debugfs` directory creation to fail silently for the new VM.
- [Low] The MAINTAINERS file was not updated to include the new ABI header `include/linux/kho/abi/kvm.h`, contrary to the commit message.
--
--- Patch [5]: [PATCH v4 05/11] KVM: LUO: Support VM preservation across live updates ---
commit 650c9d9cd02da64dbc0cdbd5b8073f5b00dadf99
Author: Tarun Sahu <tarunsahu@google.com>
KVM: LUO: Support VM preservation across live updates
Register a Live Update Orchestrator (LUO) file handler for KVM VM files to
serialize and deserialize VM state across kexec live updates. Preserves VM
type and recreates the KVM VM file during retrieval with an internally
generated fdname.
> Also updates MAINTAINERS to include virt/kvm/kvm_luo.c and
> include/linux/kho/abi/kvm.h.
[Severity: Low]
This isn't a bug, but the commit message states that the MAINTAINERS file
is updated to include include/linux/kho/abi/kvm.h, but the diff only
adds virt/kvm/kvm_luo.c. Should the header file be added to the KVM LIVE
UPDATE section?
> diff --git a/virt/kvm/kvm_luo.c b/virt/kvm/kvm_luo.c
> new file mode 100644
> index 0000000000000..025b53151b6a0
> --- /dev/null
> +++ b/virt/kvm/kvm_luo.c
[ ... ]
> +static atomic_t restored_vm_id = ATOMIC_INIT(0);
> +
> +static int kvm_luo_retrieve(struct liveupdate_file_op_args *args)
> +{
> + char fdname[ITOA_MAX_LEN + 1];
> + struct kvm_luo_ser *ser;
> + struct file *file;
> + struct kvm *kvm;
> + int err = 0;
> +
> + if (!args->serialized_data)
> + return -EINVAL;
> +
> + ser = phys_to_virt(args->serialized_data);
> +
> + snprintf(fdname, sizeof(fdname), "%d",
> + atomic_inc_return(&restored_vm_id));
[Severity: Medium]
Could this generated fdname format collide with the file descriptor of a newly
created VM in the same process?
Since this uses a simple "%d" format based on a global restored_vm_id counter,
it shares the identical naming format as normal VM file descriptors.
If a process restores VMs using LUO and then creates a new VM that happens to
get assigned a file descriptor matching one of the restored VM IDs, would the
debugfs directory creation for the new VM fail silently and result in missing
VM stats?
> +
> + file = kvm_create_vm_file(ser->type, fdname);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260728121138.1103610-1-tarunsahu@google.com?part=5
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v4 06/11] KVM: guest_memfd: Move internal definitions to internal header
2026-07-28 12:11 [PATCH v4 00/11] liveupdate: kvm: Guest_memfd preservation Tarun Sahu
` (4 preceding siblings ...)
2026-07-28 12:11 ` [PATCH v4 05/11] KVM: LUO: Support VM preservation across live updates Tarun Sahu
@ 2026-07-28 12:11 ` Tarun Sahu
2026-07-28 12:11 ` [PATCH v4 07/11] KVM: guest_memfd: Add support for freezing mappings Tarun Sahu
` (4 subsequent siblings)
10 siblings, 0 replies; 18+ messages in thread
From: Tarun Sahu @ 2026-07-28 12:11 UTC (permalink / raw)
To: ackerleytng, fuad.tabba, Andrew Morton, seanjc, dmatlack,
Shuah Khan, Jonathan Corbet, david, Tarun Sahu, Pasha Tatashin,
Pratyush Yadav, sagis, Paolo Bonzini, Mike Rapoport,
Alexander Graf
Cc: linux-kselftest, andre.przywara, michael.roth, linux-kernel,
linux-mm, will, vannapurve, maz, fvdl, kvm, oliver.upton, kvmarm,
alexandru.elisei, skhawaja, aneesh.kumar, linux-doc,
David Hildenbrand, yan.y.zhao, kexec, suzuki.poulose
Extract 'struct gmem_file', 'struct gmem_inode', and GMEM_I() from
virt/kvm/guest_memfd.c into a new internal header virt/kvm/guest_memfd.h.
Also split __kvm_gmem_create() to expose a non-static
__kvm_gmem_create_file() helper that returns a 'struct file *' instead of
an fd.
These internal definitions and helpers allow upcoming guest_memfd Live
Update Orchestrator (LUO) preservation code to access guest_memfd
internals and reconstruct guest_memfd file instances from preserved state
without installing them into a file descriptor table up front.
Signed-off-by: Tarun Sahu <tarunsahu@google.com>
---
virt/kvm/guest_memfd.c | 68 +++++++++++++++++-------------------------
virt/kvm/guest_memfd.h | 39 ++++++++++++++++++++++++
2 files changed, 67 insertions(+), 40 deletions(-)
create mode 100644 virt/kvm/guest_memfd.h
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index db57c5766ab6..dd84bba8974b 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -7,38 +7,12 @@
#include <linux/mempolicy.h>
#include <linux/pseudo_fs.h>
#include <linux/pagemap.h>
+#include "guest_memfd.h"
#include "kvm_mm.h"
static struct vfsmount *kvm_gmem_mnt;
-/*
- * A guest_memfd instance can be associated multiple VMs, each with its own
- * "view" of the underlying physical memory.
- *
- * The gmem's inode is effectively the raw underlying physical storage, and is
- * used to track properties of the physical memory, while each gmem file is
- * effectively a single VM's view of that storage, and is used to track assets
- * specific to its associated VM, e.g. memslots=>gmem bindings.
- */
-struct gmem_file {
- struct kvm *kvm;
- struct xarray bindings;
- struct list_head entry;
-};
-
-struct gmem_inode {
- struct shared_policy policy;
- struct inode vfs_inode;
- struct list_head gmem_file_list;
-
- u64 flags;
-};
-
-static __always_inline struct gmem_inode *GMEM_I(struct inode *inode)
-{
- return container_of(inode, struct gmem_inode, vfs_inode);
-}
#define kvm_gmem_for_each_file(f, inode) \
list_for_each_entry(f, &GMEM_I(inode)->gmem_file_list, entry)
@@ -557,23 +531,17 @@ bool __weak kvm_arch_supports_gmem_init_shared(struct kvm *kvm)
return true;
}
-static int __kvm_gmem_create(struct kvm *kvm, loff_t size, u64 flags)
+struct file *__kvm_gmem_create_file(struct kvm *kvm, loff_t size, u64 flags)
{
static const char *name = "[kvm-gmem]";
struct gmem_file *f;
struct inode *inode;
struct file *file;
- int fd, err;
-
- fd = get_unused_fd_flags(0);
- if (fd < 0)
- return fd;
+ int err;
f = kzalloc_obj(*f);
- if (!f) {
- err = -ENOMEM;
- goto err_fd;
- }
+ if (!f)
+ return ERR_PTR(-ENOMEM);
/* __fput() will take care of fops_put(). */
if (!fops_get(&kvm_gmem_fops)) {
@@ -612,8 +580,7 @@ static int __kvm_gmem_create(struct kvm *kvm, loff_t size, u64 flags)
xa_init(&f->bindings);
list_add(&f->entry, &GMEM_I(inode)->gmem_file_list);
- fd_install(fd, file);
- return fd;
+ return file;
err_inode:
iput(inode);
@@ -621,7 +588,28 @@ static int __kvm_gmem_create(struct kvm *kvm, loff_t size, u64 flags)
fops_put(&kvm_gmem_fops);
err_gmem:
kfree(f);
-err_fd:
+ return ERR_PTR(err);
+}
+
+static int __kvm_gmem_create(struct kvm *kvm, loff_t size, u64 flags)
+{
+ struct file *file;
+ int fd, err;
+
+ fd = get_unused_fd_flags(0);
+ if (fd < 0)
+ return fd;
+
+ file = __kvm_gmem_create_file(kvm, size, flags);
+ if (IS_ERR(file)) {
+ err = PTR_ERR(file);
+ goto err_put_fd;
+ }
+
+ fd_install(fd, file);
+ return fd;
+
+err_put_fd:
put_unused_fd(fd);
return err;
}
diff --git a/virt/kvm/guest_memfd.h b/virt/kvm/guest_memfd.h
new file mode 100644
index 000000000000..c528b046dd69
--- /dev/null
+++ b/virt/kvm/guest_memfd.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __KVM_GUEST_MEMFD_H__
+#define __KVM_GUEST_MEMFD_H__ 1
+
+#include <linux/kvm_host.h>
+#include <linux/fs.h>
+#include <linux/mempolicy.h>
+
+/*
+ * A guest_memfd instance can be associated multiple VMs, each with its own
+ * "view" of the underlying physical memory.
+ *
+ * The gmem's inode is effectively the raw underlying physical storage, and is
+ * used to track properties of the physical memory, while each gmem file is
+ * effectively a single VM's view of that storage, and is used to track assets
+ * specific to its associated VM, e.g. memslots=>gmem bindings.
+ */
+struct gmem_file {
+ struct kvm *kvm;
+ struct xarray bindings;
+ struct list_head entry;
+};
+
+struct gmem_inode {
+ struct shared_policy policy;
+ struct inode vfs_inode;
+ struct list_head gmem_file_list;
+
+ u64 flags;
+};
+
+static inline struct gmem_inode *GMEM_I(struct inode *inode)
+{
+ return container_of(inode, struct gmem_inode, vfs_inode);
+}
+
+struct file *__kvm_gmem_create_file(struct kvm *kvm, loff_t size, u64 flags);
+
+#endif /* __KVM_GUEST_MEMFD_H__ */
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v4 07/11] KVM: guest_memfd: Add support for freezing mappings
2026-07-28 12:11 [PATCH v4 00/11] liveupdate: kvm: Guest_memfd preservation Tarun Sahu
` (5 preceding siblings ...)
2026-07-28 12:11 ` [PATCH v4 06/11] KVM: guest_memfd: Move internal definitions to internal header Tarun Sahu
@ 2026-07-28 12:11 ` Tarun Sahu
2026-07-28 12:20 ` sashiko-bot
2026-07-28 12:11 ` [PATCH v4 08/11] KVM: guest_memfd: Add support for preservation via LUO Tarun Sahu
` (3 subsequent siblings)
10 siblings, 1 reply; 18+ messages in thread
From: Tarun Sahu @ 2026-07-28 12:11 UTC (permalink / raw)
To: ackerleytng, fuad.tabba, Andrew Morton, seanjc, dmatlack,
Shuah Khan, Jonathan Corbet, david, Tarun Sahu, Pasha Tatashin,
Pratyush Yadav, sagis, Paolo Bonzini, Mike Rapoport,
Alexander Graf
Cc: linux-kselftest, andre.przywara, michael.roth, linux-kernel,
linux-mm, will, vannapurve, maz, fvdl, kvm, oliver.upton, kvmarm,
alexandru.elisei, skhawaja, aneesh.kumar, linux-doc,
David Hildenbrand, yan.y.zhao, kexec, suzuki.poulose
Introduce kvm_gmem_freeze() to freeze a guest_memfd inode's mapping,
which prevents fallocate() operations and new page fault allocations
during preservation.
Use a global SRCU (`kvm_gmem_freeze_srcu`) to synchronize freeze state
checkers without incurring per-fault locking overhead or risking per-CPU
memory exhaustion (as per-CPU structure not counted in cgroup) from
per-inode SRCU structures by faulty/compromised VMM.
Signed-off-by: Tarun Sahu <tarunsahu@google.com>
---
virt/kvm/guest_memfd.c | 117 +++++++++++++++++++++++++++++++++++++----
virt/kvm/guest_memfd.h | 5 ++
2 files changed, 111 insertions(+), 11 deletions(-)
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index dd84bba8974b..d522cc34f476 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -7,11 +7,13 @@
#include <linux/mempolicy.h>
#include <linux/pseudo_fs.h>
#include <linux/pagemap.h>
+#include <linux/srcu.h>
#include "guest_memfd.h"
#include "kvm_mm.h"
static struct vfsmount *kvm_gmem_mnt;
+static struct srcu_struct kvm_gmem_freeze_srcu;
#define kvm_gmem_for_each_file(f, inode) \
@@ -96,6 +98,7 @@ static struct folio *kvm_gmem_get_folio(struct inode *inode, pgoff_t index)
/* TODO: Support huge pages. */
struct mempolicy *policy;
struct folio *folio;
+ int idx;
/*
* Fast-path: See if folio is already present in mapping to avoid
@@ -105,12 +108,20 @@ static struct folio *kvm_gmem_get_folio(struct inode *inode, pgoff_t index)
if (!IS_ERR(folio))
return folio;
+ idx = srcu_read_lock(&kvm_gmem_freeze_srcu);
+ if (kvm_gmem_is_frozen(inode)) {
+ srcu_read_unlock(&kvm_gmem_freeze_srcu, idx);
+ return ERR_PTR(-EPERM);
+ }
+
policy = mpol_shared_policy_lookup(&GMEM_I(inode)->policy, index);
folio = __filemap_get_folio_mpol(inode->i_mapping, index,
FGP_LOCK | FGP_CREAT,
mapping_gfp_mask(inode->i_mapping), policy);
mpol_cond_put(policy);
+ srcu_read_unlock(&kvm_gmem_freeze_srcu, idx);
+
/*
* External interfaces like kvm_gmem_get_pfn() support dealing
* with hugepages to a degree, but internally, guest_memfd currently
@@ -273,16 +284,30 @@ static long kvm_gmem_allocate(struct inode *inode, loff_t offset, loff_t len)
static long kvm_gmem_fallocate(struct file *file, int mode, loff_t offset,
loff_t len)
{
+ struct inode *inode = file_inode(file);
int ret;
+ int idx;
- if (!(mode & FALLOC_FL_KEEP_SIZE))
- return -EOPNOTSUPP;
+ idx = srcu_read_lock(&kvm_gmem_freeze_srcu);
+ if (kvm_gmem_is_frozen(inode)) {
+ srcu_read_unlock(&kvm_gmem_freeze_srcu, idx);
+ return -EPERM;
+ }
- if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
- return -EOPNOTSUPP;
+ if (!(mode & FALLOC_FL_KEEP_SIZE)) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
- if (!PAGE_ALIGNED(offset) || !PAGE_ALIGNED(len))
- return -EINVAL;
+ if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE)) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (!PAGE_ALIGNED(offset) || !PAGE_ALIGNED(len)) {
+ ret = -EINVAL;
+ goto out;
+ }
if (mode & FALLOC_FL_PUNCH_HOLE)
ret = kvm_gmem_punch_hole(file_inode(file), offset, len);
@@ -291,6 +316,9 @@ static long kvm_gmem_fallocate(struct file *file, int mode, loff_t offset,
if (!ret)
file_modified(file);
+
+out:
+ srcu_read_unlock(&kvm_gmem_freeze_srcu, idx);
return ret;
}
@@ -948,7 +976,9 @@ static void kvm_gmem_destroy_inode(struct inode *inode)
static void kvm_gmem_free_inode(struct inode *inode)
{
- kmem_cache_free(kvm_gmem_inode_cachep, GMEM_I(inode));
+ struct gmem_inode *gi = GMEM_I(inode);
+
+ kmem_cache_free(kvm_gmem_inode_cachep, gi);
}
static const struct super_operations kvm_gmem_super_operations = {
@@ -1003,12 +1033,21 @@ int kvm_gmem_init(struct module *module)
if (!kvm_gmem_inode_cachep)
return -ENOMEM;
+ ret = init_srcu_struct(&kvm_gmem_freeze_srcu);
+ if (ret)
+ goto err_cache;
+
ret = kvm_gmem_init_mount();
- if (ret) {
- kmem_cache_destroy(kvm_gmem_inode_cachep);
- return ret;
- }
+ if (ret)
+ goto err_srcu;
+
return 0;
+
+err_srcu:
+ cleanup_srcu_struct(&kvm_gmem_freeze_srcu);
+err_cache:
+ kmem_cache_destroy(kvm_gmem_inode_cachep);
+ return ret;
}
void kvm_gmem_exit(void)
@@ -1016,5 +1055,61 @@ void kvm_gmem_exit(void)
kern_unmount(kvm_gmem_mnt);
kvm_gmem_mnt = NULL;
rcu_barrier();
+ cleanup_srcu_struct(&kvm_gmem_freeze_srcu);
kmem_cache_destroy(kvm_gmem_inode_cachep);
}
+
+/**
+ * kvm_gmem_freeze - Freeze or unfreeze a guest_memfd inode mapping.
+ * @inode: The guest_memfd inode.
+ * @freeze: True to freeze, false to unfreeze.
+ *
+ * This API is used strictly during the live update / preservation transition
+ * window to prevent host userspace and guest-side faults from making any
+ * mapping modifications (such as fallocate or page fault allocation)
+ * to the guest_memfd page cache.
+ *
+ * Synchronization Strategy (Sleepable RCU):
+ * To avoid high-contention VFS locks (like inode_lock or
+ * filemap_invalidate_lock) on the vCPU page fault hot paths, this subsystem
+ * implements a lightweight, system-wide Sleepable RCU (SRCU) mechanism
+ * (`kvm_gmem_freeze_srcu`):
+ *
+ * Global vs. Per-Inode SRCU
+ * ======================
+ * A single system-wide global static `srcu_struct` is used instead of a
+ * per-inode SRCU structure to completely prevent unprivileged users from
+ * exhausting the host's per-CPU memory allocator. Because
+ * `init_srcu_struct()` allocates per-CPU memory via `alloc_percpu()`, which
+ * is not accounted by memory cgroups (memcg),
+ * a per-inode SRCU structure would allow a tenant to bypass cgroup limits and
+ * trigger a system-wide Out-of-Memory (OOM) crash simply by spawning a large
+ * number of guest_memfd file descriptors (bounded only by RLIMIT_NOFILE).
+ *
+ * Flag Modification Note:
+ * Since `GUEST_MEMFD_F_MAPPING_FROZEN` is the ONLY flag in
+ * `GMEM_I(inode)->flags` that is mutated dynamically at runtime (all other
+ * flags are creation-time flags which remain strictly read-only), there is
+ * no possibility of concurrent bit-modification races. Therefore, a standard
+ * `WRITE_ONCE` is fully safe and does not require complex `cmpxchg`
+ * synchronization loops.
+ */
+void kvm_gmem_freeze(struct inode *inode, bool freeze)
+{
+ u64 flags = READ_ONCE(GMEM_I(inode)->flags);
+
+ if (freeze)
+ flags |= GUEST_MEMFD_F_MAPPING_FROZEN;
+ else
+ flags &= ~GUEST_MEMFD_F_MAPPING_FROZEN;
+
+ WRITE_ONCE(GMEM_I(inode)->flags, flags);
+
+ if (freeze)
+ synchronize_srcu(&kvm_gmem_freeze_srcu);
+}
+
+bool kvm_gmem_is_frozen(struct inode *inode)
+{
+ return READ_ONCE(GMEM_I(inode)->flags) & GUEST_MEMFD_F_MAPPING_FROZEN;
+}
diff --git a/virt/kvm/guest_memfd.h b/virt/kvm/guest_memfd.h
index c528b046dd69..028c348a1023 100644
--- a/virt/kvm/guest_memfd.h
+++ b/virt/kvm/guest_memfd.h
@@ -29,11 +29,16 @@ struct gmem_inode {
u64 flags;
};
+/* Internal kernel-only flags (must not overlap with UAPI flags) */
+#define GUEST_MEMFD_F_MAPPING_FROZEN (1ULL << 63)
+
static inline struct gmem_inode *GMEM_I(struct inode *inode)
{
return container_of(inode, struct gmem_inode, vfs_inode);
}
struct file *__kvm_gmem_create_file(struct kvm *kvm, loff_t size, u64 flags);
+void kvm_gmem_freeze(struct inode *inode, bool freeze);
+bool kvm_gmem_is_frozen(struct inode *inode);
#endif /* __KVM_GUEST_MEMFD_H__ */
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v4 07/11] KVM: guest_memfd: Add support for freezing mappings
2026-07-28 12:11 ` [PATCH v4 07/11] KVM: guest_memfd: Add support for freezing mappings Tarun Sahu
@ 2026-07-28 12:20 ` sashiko-bot
0 siblings, 0 replies; 18+ messages in thread
From: sashiko-bot @ 2026-07-28 12:20 UTC (permalink / raw)
To: Tarun Sahu; +Cc: Oliver Upton, Marc Zyngier, kvm, kvmarm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Holding the global `kvm_gmem_freeze_srcu` read lock during unbounded `fallocate` operations allows an unprivileged tenant to block `kvm_gmem_freeze()` indefinitely for other VMs.
--
--- Patch [7]: [PATCH v4 07/11] KVM: guest_memfd: Add support for freezing mappings ---
commit de8f141a41c06197ca510e03554977beeeb4fcdf
Author: Tarun Sahu <tarunsahu@google.com>
KVM: guest_memfd: Add support for freezing mappings
This commit introduces kvm_gmem_freeze() to freeze a guest_memfd inode's
mapping to prevent fallocate and page fault allocations during preservation.
It uses a global SRCU to synchronize freeze state checkers without incurring
per-fault locking overhead or per-CPU memory exhaustion.
> diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
> index fe1adc9ba48d1..a4d9d348e4d16 100644
> --- a/virt/kvm/guest_memfd.c
> +++ b/virt/kvm/guest_memfd.c
[ ... ]
> @@ -273,16 +284,30 @@ static long kvm_gmem_allocate(struct inode *inode, loff_t offset, loff_t len)
> static long kvm_gmem_fallocate(struct file *file, int mode, loff_t offset,
> loff_t len)
> {
> + struct inode *inode = file_inode(file);
> int ret;
> + int idx;
>
> - if (!(mode & FALLOC_FL_KEEP_SIZE))
> - return -EOPNOTSUPP;
> + idx = srcu_read_lock(&kvm_gmem_freeze_srcu);
> + if (kvm_gmem_is_frozen(inode)) {
> + srcu_read_unlock(&kvm_gmem_freeze_srcu, idx);
> + return -EPERM;
> + }
[ ... ]
> if (mode & FALLOC_FL_PUNCH_HOLE)
> ret = kvm_gmem_punch_hole(file_inode(file), offset, len);
> else
> ret = kvm_gmem_allocate(file_inode(file), offset, len);
>
> if (!ret)
> file_modified(file);
> +
> +out:
> + srcu_read_unlock(&kvm_gmem_freeze_srcu, idx);
> return ret;
> }
[Severity: High]
Does this code allow an unprivileged tenant to stall the system by holding the
global kvm_gmem_freeze_srcu read lock across unbounded fallocate operations?
If a tenant requests a massive hole punch or allocation, kvm_gmem_fallocate()
holds the read lock for the entire duration of kvm_gmem_punch_hole() or
kvm_gmem_allocate().
If another VM triggers kvm_gmem_freeze(..., true) during this time, it will
block in synchronize_srcu(&kvm_gmem_freeze_srcu) until the massive operation
completes.
Could this lead to a cross-tenant denial of service where one VM blocks live
updates and preservation operations for all other VMs on the same hypervisor?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260728121138.1103610-1-tarunsahu@google.com?part=7
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v4 08/11] KVM: guest_memfd: Add support for preservation via LUO
2026-07-28 12:11 [PATCH v4 00/11] liveupdate: kvm: Guest_memfd preservation Tarun Sahu
` (6 preceding siblings ...)
2026-07-28 12:11 ` [PATCH v4 07/11] KVM: guest_memfd: Add support for freezing mappings Tarun Sahu
@ 2026-07-28 12:11 ` Tarun Sahu
2026-07-28 12:23 ` sashiko-bot
2026-07-28 12:11 ` [PATCH v4 09/11] docs: liveupdate: Add documentation for VM and guest_memfd preservation Tarun Sahu
` (2 subsequent siblings)
10 siblings, 1 reply; 18+ messages in thread
From: Tarun Sahu @ 2026-07-28 12:11 UTC (permalink / raw)
To: ackerleytng, fuad.tabba, Andrew Morton, seanjc, dmatlack,
Shuah Khan, Jonathan Corbet, david, Tarun Sahu, Pasha Tatashin,
Pratyush Yadav, sagis, Paolo Bonzini, Mike Rapoport,
Alexander Graf
Cc: linux-kselftest, andre.przywara, michael.roth, linux-kernel,
linux-mm, will, vannapurve, maz, fvdl, kvm, oliver.upton, kvmarm,
alexandru.elisei, skhawaja, aneesh.kumar, linux-doc,
David Hildenbrand, yan.y.zhao, kexec, suzuki.poulose
Register a Live Update Orchestrator (LUO) file handler for guest_memfd
files to preserve guest memory across kexec reboots.
Freeze the guest_memfd inode via kvm_gmem_freeze() during preservation to
prevent concurrent fallocate() calls or page fault allocations. Walk the
inode's page cache folios to serialize their metadata and physical PFNs
for kexec handover.
On retrieval, reconstruct the guest_memfd file with __kvm_gmem_create_file(),
re-insert the preserved folios into the page cache via filemap_add_folio(),
and associate the file back to its preserved KVM VM via the VM token.
Currently, this supports fully shared guest_memfd instances backed by
PAGE_SIZE pages. Also update MAINTAINERS for guest_memfd LUO preservation.
Signed-off-by: Tarun Sahu <tarunsahu@google.com>
---
MAINTAINERS | 1 +
include/linux/kho/abi/kvm.h | 79 +++++-
virt/kvm/Makefile.kvm | 2 +-
virt/kvm/guest_memfd_luo.c | 515 ++++++++++++++++++++++++++++++++++++
virt/kvm/kvm_main.c | 7 +
virt/kvm/kvm_mm.h | 4 +
6 files changed, 601 insertions(+), 7 deletions(-)
create mode 100644 virt/kvm/guest_memfd_luo.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 0283f0fd6ef4..8db8bb448b23 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14548,6 +14548,7 @@ L: kexec@lists.infradead.org
L: kvm@vger.kernel.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux.git
+F: virt/kvm/guest_memfd_luo.c
F: virt/kvm/kvm_luo.c
KVM PARAVIRT (KVM/paravirt)
diff --git a/include/linux/kho/abi/kvm.h b/include/linux/kho/abi/kvm.h
index 718db68a541a..42074d76e04a 100644
--- a/include/linux/kho/abi/kvm.h
+++ b/include/linux/kho/abi/kvm.h
@@ -9,20 +9,23 @@
#define _LINUX_KHO_ABI_KVM_H
#include <linux/types.h>
+#include <linux/bits.h>
#include <linux/kho/abi/kexec_handover.h>
/**
- * DOC: KVM Live Update ABI
+ * DOC: KVM and guest_memfd Live Update ABI
*
- * KVM uses the ABI defined below for preserving its state
+ * KVM and guest_memfd use the ABI defined below for preserving their states
* across a kexec reboot using the LUO.
*
- * The state is serialized into a packed structure `struct kvm_luo_ser`
- * which is handed over to the next kernel via the KHO mechanism.
+ * The state is serialized into packed structures (struct kvm_luo_ser and
+ * struct guest_memfd_luo_ser) which are handed over to the next kernel via
+ * the KHO mechanism.
*
- * This interface is a contract. Any modification to the structure layout
+ * This interface is a contract. Any modification to the structure layouts
* constitutes a breaking change. Such changes require incrementing the
- * version number in the KVM_LUO_FH_COMPATIBLE compatibility string.
+ * version number in the KVM_LUO_FH_COMPATIBLE or
+ * GUEST_MEMFD_LUO_FH_COMPATIBLE compatibility strings.
*/
/**
@@ -36,4 +39,68 @@ struct kvm_luo_ser {
/* The compatibility string for KVM VM file handler */
#define KVM_LUO_FH_COMPATIBLE "kvm_vm_luo_v1"
+/**
+ * struct guest_memfd_luo_folio_ser - Serialization layout for a single folio in guest_memfd.
+ * @pfn: Page Frame Number of the folio.
+ * @index: Page offset of the folio within the file.
+ * @flags: State flags associated with the folio.
+ */
+struct guest_memfd_luo_folio_ser {
+ u64 pfn:52;
+ u64 flags:12;
+ u64 index;
+} __packed;
+
+/**
+ * GUEST_MEMFD_LUO_FOLIO_UPTODATE - The folio is up-to-date.
+ *
+ * This flag is per folio to check if the folio is uptodate.
+ */
+#define GUEST_MEMFD_LUO_FOLIO_UPTODATE BIT(0)
+
+
+/**
+ * GUEST_MEMFD_LUO_FLAG_MMAP - The guest_memfd supports mmap.
+ *
+ * This flag indicates that the guest_memfd supports host-side mmap.
+ */
+#define GUEST_MEMFD_LUO_FLAG_MMAP BIT(0)
+
+/**
+ * GUEST_MEMFD_LUO_FLAG_INIT_SHARED - Initialize memory as shared.
+ *
+ * This flag indicates that the guest_memfd has been initialized as shared
+ * memory.
+ */
+#define GUEST_MEMFD_LUO_FLAG_INIT_SHARED BIT(1)
+
+/**
+ * GUEST_MEMFD_LUO_SUPPORTED_FLAGS - Supported guest_memfd LUO flags mask.
+ *
+ * A mask of all guest_memfd preservation flags supported by this version
+ * of the KVM LUO ABI.
+ */
+#define GUEST_MEMFD_LUO_SUPPORTED_FLAGS (GUEST_MEMFD_LUO_FLAG_MMAP | \
+ GUEST_MEMFD_LUO_FLAG_INIT_SHARED)
+
+/**
+ * struct guest_memfd_luo_ser - Main serialization structure for guest_memfd.
+ * @size: The size of the file in bytes.
+ * @flags: File-level flags.
+ * @nr_folios: Number of folios in the folios array.
+ * @vm_token: Token of the associated KVM VM instance.
+ * @folios: KHO vmalloc descriptor pointing to the array of
+ * struct guest_memfd_luo_folio_ser.
+ */
+struct guest_memfd_luo_ser {
+ u64 size;
+ u64 flags;
+ u64 nr_folios;
+ u64 vm_token;
+ struct kho_vmalloc folios;
+} __packed;
+
+/* The compatibility string for GUEST_MEMFD file handler */
+#define GUEST_MEMFD_LUO_FH_COMPATIBLE "guest_memfd_luo_v1"
+
#endif /* _LINUX_KHO_ABI_KVM_H */
diff --git a/virt/kvm/Makefile.kvm b/virt/kvm/Makefile.kvm
index c1a962159264..d30fca094c42 100644
--- a/virt/kvm/Makefile.kvm
+++ b/virt/kvm/Makefile.kvm
@@ -13,4 +13,4 @@ kvm-$(CONFIG_HAVE_KVM_IRQ_ROUTING) += $(KVM)/irqchip.o
kvm-$(CONFIG_HAVE_KVM_DIRTY_RING) += $(KVM)/dirty_ring.o
kvm-$(CONFIG_HAVE_KVM_PFNCACHE) += $(KVM)/pfncache.o
kvm-$(CONFIG_KVM_GUEST_MEMFD) += $(KVM)/guest_memfd.o
-kvm-$(CONFIG_LIVEUPDATE_GUEST_MEMFD) += $(KVM)/kvm_luo.o
+kvm-$(CONFIG_LIVEUPDATE_GUEST_MEMFD) += $(KVM)/guest_memfd_luo.o $(KVM)/kvm_luo.o
diff --git a/virt/kvm/guest_memfd_luo.c b/virt/kvm/guest_memfd_luo.c
new file mode 100644
index 000000000000..c0aa9354e0a7
--- /dev/null
+++ b/virt/kvm/guest_memfd_luo.c
@@ -0,0 +1,515 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (c) 2026, Google LLC.
+ * Tarun Sahu <tarunsahu@google.com>
+ *
+ * Guestmemfd Preservation for Live Update Orchestrator (LUO)
+ */
+
+/**
+ * DOC: Guestmemfd Preservation via LUO
+ *
+ * Overview
+ * ========
+ *
+ * Guest memory file descriptors (guest_memfd) can be preserved over a kexec
+ * reboot using the Live Update Orchestrator (LUO) file preservation. This
+ * allows userspace to preserve VM memory across kexec reboots.
+ *
+ * The preservation is not intended to be transparent. Only select properties
+ * of the guest_memfd are preserved, while others are reset to default.
+ *
+ * Preserved Properties
+ * ====================
+ *
+ * The following properties of guest_memfd are preserved across kexec:
+ *
+ * File Size
+ * The size of the file is preserved.
+ *
+ * File Contents
+ * All folios present in the page cache are preserved.
+ *
+ * File-level Flags
+ * The file-level flags (such as MMAP support and INIT_SHARED default mapping)
+ * are preserved.
+ *
+ * Non-Preserved Properties
+ * ========================
+ *
+ * NUMA Memory Policy
+ * NUMA memory policies associated with the guest_memfd are not preserved.
+ */
+#include <linux/liveupdate.h>
+#include <linux/kvm_host.h>
+#include <linux/pagemap.h>
+#include <linux/file.h>
+#include <linux/err.h>
+#include <linux/anon_inodes.h>
+#include <linux/magic.h>
+#include <linux/kexec_handover.h>
+#include <linux/kho/abi/kexec_handover.h>
+#include <linux/kho/abi/kvm.h>
+#include "guest_memfd.h"
+#include "kvm_mm.h"
+
+
+static int kvm_gmem_luo_walk_folios(struct address_space *mapping,
+ pgoff_t end_index, struct guest_memfd_luo_folio_ser *folios_ser,
+ u64 *out_count)
+{
+ struct folio_batch fbatch;
+ pgoff_t index = 0;
+ u64 count = 0;
+ int err = 0;
+
+ folio_batch_init(&fbatch);
+ while (index < end_index) {
+ unsigned int nr, i;
+
+ nr = filemap_get_folios(mapping, &index, end_index - 1, &fbatch);
+ if (nr == 0)
+ break;
+
+ for (i = 0; i < nr; i++) {
+ struct folio *folio = fbatch.folios[i];
+
+ if (folios_ser) {
+ if (folio_test_hwpoison(folio)) {
+ err = -EHWPOISON;
+ folio_batch_release(&fbatch);
+ goto out;
+ }
+ err = kho_preserve_folio(folio);
+ if (err) {
+ folio_batch_release(&fbatch);
+ goto out;
+ }
+
+ folios_ser[count].pfn = folio_pfn(folio);
+ folios_ser[count].index = folio->index;
+ folios_ser[count].flags = folio_test_uptodate(folio) ?
+ GUEST_MEMFD_LUO_FOLIO_UPTODATE : 0;
+ }
+ count++;
+ }
+ folio_batch_release(&fbatch);
+ cond_resched();
+ }
+
+out:
+ *out_count = count;
+ return err;
+}
+
+static bool kvm_gmem_luo_can_preserve(struct liveupdate_file_handler *handler, struct file *file)
+{
+ struct inode *inode = file_inode(file);
+ struct gmem_file *gmem_file;
+ struct kvm *kvm;
+
+ if (inode->i_sb->s_magic != GUEST_MEMFD_MAGIC)
+ return false;
+
+ gmem_file = file->private_data;
+ if (!gmem_file)
+ return false;
+
+ /*
+ * Only Fully-shared guest_memfd preservation is supported
+ */
+ if (!(GMEM_I(inode)->flags & GUEST_MEMFD_FLAG_INIT_SHARED))
+ return false;
+
+ /*
+ * It makes sure that no memory can be converted to private
+ * even if it was initially fully shared (in-place conversions are
+ * prevented).
+ */
+ kvm = gmem_file->kvm;
+ if (kvm_arch_has_private_mem(kvm))
+ return false;
+
+ if (mapping_large_folio_support(inode->i_mapping))
+ return false;
+
+ return true;
+}
+
+static int kvm_gmem_luo_preserve(struct liveupdate_file_op_args *args)
+{
+ struct guest_memfd_luo_folio_ser *folios_ser = NULL;
+ u64 count = 0, gmem_flags, abi_flags = 0;
+ struct guest_memfd_luo_ser *ser;
+ struct address_space *mapping;
+ struct gmem_file *gmem_file;
+ struct inode *inode;
+ pgoff_t end_index;
+ struct kvm *kvm;
+ int err = 0;
+ long size, i;
+
+ inode = file_inode(args->file);
+ kvm_gmem_freeze(inode, true);
+
+ mapping = inode->i_mapping;
+ size = i_size_read(inode);
+ if (!size) {
+ err = -EINVAL;
+ goto err_unfreeze_inode;
+ }
+
+ if (WARN_ON_ONCE(!PAGE_ALIGNED(size))) {
+ err = -EINVAL;
+ goto err_unfreeze_inode;
+ }
+
+ gmem_file = args->file->private_data;
+ kvm = gmem_file->kvm;
+
+ gmem_flags = READ_ONCE(GMEM_I(inode)->flags);
+ if (gmem_flags & ~(GUEST_MEMFD_FLAG_MMAP | GUEST_MEMFD_FLAG_INIT_SHARED
+ | GUEST_MEMFD_F_MAPPING_FROZEN)) {
+ err = -EOPNOTSUPP;
+ goto err_unfreeze_inode;
+ }
+
+ if (gmem_flags & GUEST_MEMFD_FLAG_MMAP)
+ abi_flags |= GUEST_MEMFD_LUO_FLAG_MMAP;
+ if (gmem_flags & GUEST_MEMFD_FLAG_INIT_SHARED)
+ abi_flags |= GUEST_MEMFD_LUO_FLAG_INIT_SHARED;
+
+ end_index = size >> PAGE_SHIFT;
+
+ ser = kho_alloc_preserve(sizeof(*ser));
+ if (IS_ERR(ser)) {
+ err = PTR_ERR(ser);
+ goto err_unfreeze_inode;
+ }
+
+ /* First pass: Count the folios present in the page cache */
+ err = kvm_gmem_luo_walk_folios(mapping, end_index, NULL, &count);
+ if (err)
+ goto err_free_ser;
+
+ ser->size = size;
+ ser->flags = abi_flags;
+ ser->nr_folios = count;
+
+ /* VM Token will be set during the kvm_gmem_luo_freeze() */
+ ser->vm_token = 0;
+
+ if (count > 0) {
+ folios_ser = vcalloc(count, sizeof(*folios_ser));
+ if (!folios_ser) {
+ err = -ENOMEM;
+ goto err_free_ser;
+ }
+
+ /* Second pass: Fill the metadata array and preserve folios */
+ err = kvm_gmem_luo_walk_folios(mapping, end_index, folios_ser, &count);
+ if (err)
+ goto err_unpreserve_unlocked;
+
+ if (WARN_ON_ONCE(count != ser->nr_folios)) {
+ err = -EINVAL;
+ goto err_unpreserve_unlocked;
+ }
+ }
+
+ if (count > 0) {
+ err = kho_preserve_vmalloc(folios_ser, &ser->folios);
+ if (err)
+ goto err_unpreserve_unlocked;
+ }
+
+ args->serialized_data = virt_to_phys(ser);
+ args->private_data = folios_ser;
+
+ return 0;
+
+err_unpreserve_unlocked:
+ for (i = (long)count - 1; i >= 0; i--) {
+ struct folio *folio;
+
+ if (!folios_ser[i].pfn)
+ continue;
+
+ folio = pfn_folio(folios_ser[i].pfn);
+ kho_unpreserve_folio(folio);
+ }
+ vfree(folios_ser);
+err_free_ser:
+ kho_unpreserve_free(ser);
+err_unfreeze_inode:
+ kvm_gmem_freeze(inode, false);
+ return err;
+}
+
+static int kvm_gmem_luo_freeze(struct liveupdate_file_op_args *args)
+{
+ struct guest_memfd_luo_ser *ser;
+ struct gmem_file *gmem_file;
+ struct kvm *kvm;
+ struct file *kvm_file;
+ u64 vm_token;
+ int err;
+
+ if (WARN_ON_ONCE(!args->serialized_data))
+ return -EINVAL;
+
+ ser = phys_to_virt(args->serialized_data);
+
+ gmem_file = args->file->private_data;
+ kvm = gmem_file->kvm;
+
+ /*
+ * Obtain a strong reference to kvm->vm_file to prevent the SLAB_TYPESAFE_BY_RCU
+ * file memory from being reallocated while it is being processed.
+ */
+ kvm_file = get_file_active(&kvm->vm_file);
+ if (!kvm_file)
+ return -ENOENT;
+
+ err = liveupdate_get_token_outgoing(args->session, kvm_file, &vm_token);
+ fput(kvm_file);
+ if (err)
+ return err;
+
+ ser->vm_token = vm_token;
+ return 0;
+}
+
+static void kvm_gmem_luo_discard_folios(
+ const struct guest_memfd_luo_folio_ser *folios_ser,
+ u64 nr_folios, u64 start_idx)
+{
+ long i;
+
+ for (i = start_idx; i < nr_folios; i++) {
+ struct folio *folio;
+ phys_addr_t phys;
+
+ if (!folios_ser[i].pfn)
+ continue;
+
+ phys = PFN_PHYS(folios_ser[i].pfn);
+ folio = kho_restore_folio(phys);
+ if (folio)
+ folio_put(folio);
+ }
+}
+
+static void kvm_gmem_luo_unpreserve(struct liveupdate_file_op_args *args)
+{
+ struct guest_memfd_luo_folio_ser *folios_ser = args->private_data;
+ struct guest_memfd_luo_ser *ser;
+ long i;
+
+ if (WARN_ON_ONCE(!args->serialized_data))
+ return;
+
+ ser = phys_to_virt(args->serialized_data);
+
+ if (ser->nr_folios > 0)
+ kho_unpreserve_vmalloc(&ser->folios);
+ for (i = ser->nr_folios - 1; i >= 0; i--) {
+ struct folio *folio;
+
+ if (!folios_ser[i].pfn)
+ continue;
+
+ folio = pfn_folio(folios_ser[i].pfn);
+ kho_unpreserve_folio(folio);
+ }
+ vfree(folios_ser);
+
+ kho_unpreserve_free(ser);
+ kvm_gmem_freeze(file_inode(args->file), false);
+}
+
+static int kvm_gmem_luo_retrieve(struct liveupdate_file_op_args *args)
+{
+ struct guest_memfd_luo_folio_ser *folios_ser = NULL;
+ struct guest_memfd_luo_ser *ser;
+ struct kvm *kvm = NULL;
+ struct file *vm_file;
+ struct inode *inode;
+ struct file *file;
+ u64 gmem_flags = 0;
+ int err = 0;
+ u64 i = 0;
+
+ if (!args->serialized_data)
+ return -EINVAL;
+
+ ser = phys_to_virt(args->serialized_data);
+
+ if (ser->size <= 0 || !PAGE_ALIGNED(ser->size)) {
+ err = -EINVAL;
+ goto err_free_ser;
+ }
+
+ if (ser->flags & ~GUEST_MEMFD_LUO_SUPPORTED_FLAGS) {
+ err = -EOPNOTSUPP;
+ goto err_free_ser;
+ }
+
+ if (ser->flags & GUEST_MEMFD_LUO_FLAG_MMAP)
+ gmem_flags |= GUEST_MEMFD_FLAG_MMAP;
+ if (ser->flags & GUEST_MEMFD_LUO_FLAG_INIT_SHARED)
+ gmem_flags |= GUEST_MEMFD_FLAG_INIT_SHARED;
+
+ err = liveupdate_get_file_incoming(args->session, ser->vm_token, &vm_file);
+ if (err) {
+ pr_warn("gmem: provided VM FD token (%llx) on preserve is incorrect\n",
+ ser->vm_token);
+ goto err_free_ser;
+ }
+
+ if (file_is_kvm(vm_file))
+ kvm = vm_file->private_data;
+
+ /*
+ * Release the temporary reference taken by the liveupdate_get_file_incoming
+ * call. LUO still holds a reference.
+ */
+ fput(vm_file);
+
+ if (!kvm) {
+ err = -EINVAL;
+ goto err_free_ser;
+ }
+
+ if (gmem_flags & ~kvm_gmem_get_supported_flags(kvm)) {
+ err = -EINVAL;
+ goto err_free_ser;
+ }
+
+ file = __kvm_gmem_create_file(kvm, ser->size, gmem_flags);
+ if (IS_ERR(file)) {
+ err = PTR_ERR(file);
+ goto err_free_ser;
+ }
+
+ inode = file_inode(file);
+
+ if (ser->nr_folios) {
+ folios_ser = kho_restore_vmalloc(&ser->folios);
+ if (!folios_ser) {
+ err = -EINVAL;
+ goto err_destroy_file;
+ }
+
+ for (i = 0; i < ser->nr_folios; i++) {
+ struct folio *folio;
+ phys_addr_t phys;
+
+ if (!folios_ser[i].pfn)
+ continue;
+
+ phys = PFN_PHYS(folios_ser[i].pfn);
+ folio = kho_restore_folio(phys);
+ if (!folio) {
+ pr_err("gmem: failed to restore folio at %llx\n", phys);
+ err = -EIO;
+ goto err_put_remaining_folios;
+ }
+
+ err = filemap_add_folio(inode->i_mapping, folio, folios_ser[i].index,
+ GFP_KERNEL);
+ if (err) {
+ pr_err("gmem: failed to add folio to page cache\n");
+ folio_put(folio);
+ goto err_put_remaining_folios;
+ }
+
+ if (folios_ser[i].flags & GUEST_MEMFD_LUO_FOLIO_UPTODATE)
+ folio_mark_uptodate(folio);
+ folio_unlock(folio);
+ folio_put(folio);
+ }
+ vfree(folios_ser);
+ }
+
+ args->file = file;
+ kho_restore_free(ser);
+ return 0;
+
+err_put_remaining_folios:
+ i++;
+err_destroy_file:
+ fput(file);
+err_free_ser:
+ if (ser->nr_folios) {
+ if (!folios_ser)
+ folios_ser = kho_restore_vmalloc(&ser->folios);
+ if (folios_ser) {
+ kvm_gmem_luo_discard_folios(folios_ser, ser->nr_folios, i);
+ vfree(folios_ser);
+ }
+ }
+ kho_restore_free(ser);
+ return err;
+}
+
+static void kvm_gmem_luo_finish(struct liveupdate_file_op_args *args)
+{
+ struct guest_memfd_luo_ser *ser;
+ struct guest_memfd_luo_folio_ser *folios_ser;
+
+ /* Nothing to be done here, if retrieve_status was successful or errored,
+ * Cleanup is taken care of in retrieval call.
+ */
+ if (args->retrieve_status)
+ return;
+
+ if (!args->serialized_data)
+ return;
+
+ ser = phys_to_virt(args->serialized_data);
+
+ if (ser->nr_folios) {
+ folios_ser = kho_restore_vmalloc(&ser->folios);
+ if (folios_ser) {
+ kvm_gmem_luo_discard_folios(folios_ser, ser->nr_folios, 0);
+ vfree(folios_ser);
+ }
+ }
+
+ kho_restore_free(ser);
+}
+
+static const struct liveupdate_file_ops kvm_gmem_luo_file_ops = {
+ .can_preserve = kvm_gmem_luo_can_preserve,
+ .preserve = kvm_gmem_luo_preserve,
+ .freeze = kvm_gmem_luo_freeze,
+ .retrieve = kvm_gmem_luo_retrieve,
+ .unpreserve = kvm_gmem_luo_unpreserve,
+ .finish = kvm_gmem_luo_finish,
+ .owner = THIS_MODULE,
+};
+
+static struct liveupdate_file_handler kvm_gmem_luo_handler = {
+ .ops = &kvm_gmem_luo_file_ops,
+ .compatible = GUEST_MEMFD_LUO_FH_COMPATIBLE,
+};
+
+int kvm_gmem_luo_init(void)
+{
+ int err = liveupdate_register_file_handler(&kvm_gmem_luo_handler);
+
+ if (err && err != -EOPNOTSUPP) {
+ pr_err("Could not register luo filesystem handler: %pe\n", ERR_PTR(err));
+ return err;
+ }
+
+ return 0;
+}
+
+void kvm_gmem_luo_exit(void)
+{
+ liveupdate_unregister_file_handler(&kvm_gmem_luo_handler);
+}
+
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index d9c3dd1afb05..e8e2f10cd71e 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -6581,6 +6581,10 @@ int kvm_init(unsigned vcpu_size, unsigned vcpu_align, struct module *module)
if (r)
goto err_luo;
+ r = kvm_gmem_luo_init();
+ if (r)
+ goto err_gmem_luo;
+
/*
* Registration _must_ be the very last thing done, as this exposes
* /dev/kvm to userspace, i.e. all infrastructure must be setup!
@@ -6594,6 +6598,8 @@ int kvm_init(unsigned vcpu_size, unsigned vcpu_align, struct module *module)
return 0;
err_register:
+ kvm_gmem_luo_exit();
+err_gmem_luo:
kvm_luo_exit();
err_luo:
kvm_uninit_virtualization();
@@ -6625,6 +6631,7 @@ void kvm_exit(void)
*/
misc_deregister(&kvm_dev);
+ kvm_gmem_luo_exit();
kvm_luo_exit();
kvm_uninit_virtualization();
diff --git a/virt/kvm/kvm_mm.h b/virt/kvm/kvm_mm.h
index 87198715fb01..1295ff8bd504 100644
--- a/virt/kvm/kvm_mm.h
+++ b/virt/kvm/kvm_mm.h
@@ -103,9 +103,13 @@ static inline void kvm_gmem_unbind(struct kvm_memory_slot *slot)
#ifdef CONFIG_LIVEUPDATE_GUEST_MEMFD
int kvm_luo_init(void);
void kvm_luo_exit(void);
+int kvm_gmem_luo_init(void);
+void kvm_gmem_luo_exit(void);
#else
static inline int kvm_luo_init(void) { return 0; }
static inline void kvm_luo_exit(void) {}
+static inline int kvm_gmem_luo_init(void) { return 0; }
+static inline void kvm_gmem_luo_exit(void) {}
#endif /* CONFIG_LIVEUPDATE_GUEST_MEMFD */
#endif /* __KVM_MM_H__ */
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v4 08/11] KVM: guest_memfd: Add support for preservation via LUO
2026-07-28 12:11 ` [PATCH v4 08/11] KVM: guest_memfd: Add support for preservation via LUO Tarun Sahu
@ 2026-07-28 12:23 ` sashiko-bot
0 siblings, 0 replies; 18+ messages in thread
From: sashiko-bot @ 2026-07-28 12:23 UTC (permalink / raw)
To: Tarun Sahu; +Cc: kvm, kvmarm, Oliver Upton, Marc Zyngier
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] `kvm_gmem_luo_finish` incorrectly performs cleanup on successful retrieval, resulting in use-after-free and double-frees of the serialization state and premature freeing of page cache folios.
- [High] Missing `kho_is_enabled()` check before invoking KHO serialization APIs, risking a NULL pointer dereference.
--
--- Patch [8]: [PATCH v4 08/11] KVM: guest_memfd: Add support for preservation via LUO ---
commit 8d81cd3b20bca211fdf1860f352a2bbcf1096ec9
Author: Tarun Sahu <tarunsahu@google.com>
KVM: guest_memfd: Add support for preservation via LUO
This commit registers a Live Update Orchestrator file handler for
guest_memfd files to preserve guest memory across kexec reboots. It freezes
the guest_memfd inode during preservation, walks the page cache to serialize
metadata, and reconstructs the file and its folios on retrieval.
> diff --git a/virt/kvm/guest_memfd_luo.c b/virt/kvm/guest_memfd_luo.c
> new file mode 100644
> index 0000000000000..c0aa9354e0a78
> --- /dev/null
> +++ b/virt/kvm/guest_memfd_luo.c
> @@ -0,0 +1,515 @@
[ ... ]
> +static int kvm_gmem_luo_preserve(struct liveupdate_file_op_args *args)
> +{
> + struct guest_memfd_luo_folio_ser *folios_ser = NULL;
> + u64 count = 0, gmem_flags, abi_flags = 0;
> + struct guest_memfd_luo_ser *ser;
[ ... ]
> + end_index = size >> PAGE_SHIFT;
> +
> + ser = kho_alloc_preserve(sizeof(*ser));
[Severity: High]
Does this path need to verify if the KHO subsystem is actually enabled?
If the kernel is booted with KHO disabled, calling kho_alloc_preserve() and
other KHO APIs unconditionally can cause a NULL pointer dereference.
Could we check kho_is_enabled() either here or in kvm_gmem_luo_init() before
registering the handler?
[ ... ]
> +static void kvm_gmem_luo_finish(struct liveupdate_file_op_args *args)
> +{
> + struct guest_memfd_luo_ser *ser;
> + struct guest_memfd_luo_folio_ser *folios_ser;
> +
> + /* Nothing to be done here, if retrieve_status was successful or errored,
> + * Cleanup is taken care of in retrieval call.
> + */
> + if (args->retrieve_status)
> + return;
> +
> + if (!args->serialized_data)
> + return;
> +
> + ser = phys_to_virt(args->serialized_data);
[Severity: High]
Is the check on args->retrieve_status inverted here?
When kvm_gmem_luo_retrieve() succeeds, it returns 0 and frees the ser pointer:
kvm_gmem_luo_retrieve()
args->file = file;
kho_restore_free(ser);
return 0;
Because retrieve_status is 0 on success, this finish function will fall
through the if statement instead of returning early. It will then access
the freed ser pointer and mistakenly discard all the successfully restored
folios.
Would checking if (!args->retrieve_status) avoid this use-after-free
regression?
> +
> + if (ser->nr_folios) {
> + folios_ser = kho_restore_vmalloc(&ser->folios);
> + if (folios_ser) {
> + kvm_gmem_luo_discard_folios(folios_ser, ser->nr_folios, 0);
> + vfree(folios_ser);
> + }
> + }
> +
> + kho_restore_free(ser);
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260728121138.1103610-1-tarunsahu@google.com?part=8
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v4 09/11] docs: liveupdate: Add documentation for VM and guest_memfd preservation
2026-07-28 12:11 [PATCH v4 00/11] liveupdate: kvm: Guest_memfd preservation Tarun Sahu
` (7 preceding siblings ...)
2026-07-28 12:11 ` [PATCH v4 08/11] KVM: guest_memfd: Add support for preservation via LUO Tarun Sahu
@ 2026-07-28 12:11 ` Tarun Sahu
2026-07-28 12:21 ` sashiko-bot
2026-07-28 12:11 ` [PATCH v4 10/11] KVM: selftests: Split ____vm_create() and add vm_create_from_fd() Tarun Sahu
2026-07-28 12:11 ` [PATCH v4 11/11] KVM: selftests: Add guest_memfd_preservation_test Tarun Sahu
10 siblings, 1 reply; 18+ messages in thread
From: Tarun Sahu @ 2026-07-28 12:11 UTC (permalink / raw)
To: ackerleytng, fuad.tabba, Andrew Morton, seanjc, dmatlack,
Shuah Khan, Jonathan Corbet, david, Tarun Sahu, Pasha Tatashin,
Pratyush Yadav, sagis, Paolo Bonzini, Mike Rapoport,
Alexander Graf
Cc: linux-kselftest, andre.przywara, michael.roth, linux-kernel,
linux-mm, will, vannapurve, maz, fvdl, kvm, oliver.upton, kvmarm,
alexandru.elisei, skhawaja, aneesh.kumar, linux-doc,
David Hildenbrand, yan.y.zhao, kexec, suzuki.poulose
Add Documentation/liveupdate/vmm.rst detailing VMM requirements, scope,
ordering constraints, and kernel-doc inclusions for KVM VM and
guest_memfd preservation via Live Update Orchestrator (LUO).
Also update Documentation/core-api/liveupdate.rst and MAINTAINERS.
Signed-off-by: Tarun Sahu <tarunsahu@google.com>
---
Documentation/core-api/liveupdate.rst | 1 +
Documentation/liveupdate/vmm.rst | 107 ++++++++++++++++++++++++++
MAINTAINERS | 1 +
3 files changed, 109 insertions(+)
create mode 100644 Documentation/liveupdate/vmm.rst
diff --git a/Documentation/core-api/liveupdate.rst b/Documentation/core-api/liveupdate.rst
index 5a292d0f3706..bac58a363151 100644
--- a/Documentation/core-api/liveupdate.rst
+++ b/Documentation/core-api/liveupdate.rst
@@ -34,6 +34,7 @@ The following types of file descriptors can be preserved
:maxdepth: 1
../mm/memfd_preservation
+ ../liveupdate/vmm
Public API
==========
diff --git a/Documentation/liveupdate/vmm.rst b/Documentation/liveupdate/vmm.rst
new file mode 100644
index 000000000000..b8e8fb2b63c2
--- /dev/null
+++ b/Documentation/liveupdate/vmm.rst
@@ -0,0 +1,107 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+=============================
+VM & Guest_Memfd Preservation
+=============================
+
+.. kernel-doc:: virt/kvm/kvm_luo.c
+ :doc: KVM VM Preservation via LUO
+
+.. kernel-doc:: virt/kvm/guest_memfd_luo.c
+ :doc: Guest_Memfd Preservation via LUO
+
+VMM Instructions
+================
+
+This section describes the requirements, scope, conditions, and
+ordering constraints that a Virtual Machine Monitor (VMM) must adhere
+to for successful preservation and retrieval of guest_memfd files
+across a Live Update Orchestrator (LUO) sequence.
+
+Scope and Limitations
+---------------------
+
+At this stage, the scope of guest_memfd preservation is restricted to:
+
+1. **Fully Shared guest_memfd**:
+ At this time only fully shared guest_memfd is supported. Any system that
+ supports coco vm (which uses private guest_memfd), will not support
+ the preservation.
+
+2. **Standard Page Size**:
+ Only guest_memfd backed by standard page size (``PAGE_SIZE``,
+ order-0) pages is supported. Large/huge page backing (e.g.,
+ hugetlb guest_memfd) is not supported.
+
+Any Virtual Machine (VM) whose memory is fully backed by such
+guest_memfd files can be preserved across live update.
+
+VMM Actions and Conditions during Live Update
+---------------------------------------------
+
+During the live update sequence, the kernel introduces a *freezing*
+phase for the guest_memfd inode. Freezing prevents any modifications to
+the guest_memfd page cache. Specifically, once a guest_memfd mapping is
+frozen:
+
+- Any subsequent ``fallocate`` calls on the guest_memfd file descriptor
+ will fail and return ``-EPERM``.
+- Any new page faults (guest-side or host-userspace-side) that require
+ folio allocation will fail and return ``-EPERM``.
+
+To prevent vCPUs or VMM helper threads from failing due to these
+``-EPERM`` errors, the VMM must implement one of the following
+strategies:
+
+1. **Pause the VM (Recommended)**:
+ The VMM should pause/suspend all vCPUs before invoking the
+ preservation or freezing of the VM and guest_memfd files. This
+ ensures no new page faults or memory accesses can occur while the
+ guest_memfd is frozen.
+
+2. **Handle Fault Failures**:
+ If the VM is not paused, the VMM must be prepared to handle VM
+ exits or user page fault errors resulting from the ``-EPERM``
+ failures. The VMM must take appropriate action, such as
+ immediately pausing the VM, or aborting the live update sequence
+ (by tearing down or unpreserving the live update session).
+
+Preservation and Retrieval Ordering
+-----------------------------------
+
+Preservation Order
+~~~~~~~~~~~~~~~~~~
+
+There is no strict ordering requirement for initiating the
+preservation of the KVM VM file and the guest_memfd files; they are
+preserved independently. If kexec is triggered with guest_memfd
+preservation without preserving the vm file, kexec will fail.
+
+Retrieval Order
+~~~~~~~~~~~~~~~
+
+Similarly, there is no strict ordering required for retrieving the VM
+and guest_memfd files. Any file can be retrieved at any order.
+
+If guest_memfd file is retrieved and VM file is not retrieved, and
+luo_finish is called, then vm_file will be lost and guest_memfd file
+will be hanging around.
+
+NOTE: Before Initiating the preservation/retrieval, it is necessary to make
+sure that the kvm module is loaded (/dev/kvm must be available).
+
+
+VM & Guest_Memfd Preservation ABI
+=================================
+
+.. kernel-doc:: include/linux/kho/abi/kvm.h
+ :doc: DOC: guest_memfd Live Update ABI
+
+.. kernel-doc:: include/linux/kho/abi/kvm.h
+ :internal:
+
+See Also
+========
+
+- :doc:`/core-api/liveupdate`
+- :doc:`/userspace-api/liveupdate`
diff --git a/MAINTAINERS b/MAINTAINERS
index 8db8bb448b23..c032f9a0451b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14548,6 +14548,7 @@ L: kexec@lists.infradead.org
L: kvm@vger.kernel.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux.git
+F: Documentation/liveupdate/vmm.rst
F: virt/kvm/guest_memfd_luo.c
F: virt/kvm/kvm_luo.c
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v4 09/11] docs: liveupdate: Add documentation for VM and guest_memfd preservation
2026-07-28 12:11 ` [PATCH v4 09/11] docs: liveupdate: Add documentation for VM and guest_memfd preservation Tarun Sahu
@ 2026-07-28 12:21 ` sashiko-bot
0 siblings, 0 replies; 18+ messages in thread
From: sashiko-bot @ 2026-07-28 12:21 UTC (permalink / raw)
To: Tarun Sahu; +Cc: kvmarm, Marc Zyngier, Oliver Upton, kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Mismatched kernel-doc section titles cause Sphinx build warnings and missing documentation.
--
--- Patch [9]: [PATCH v4 09/11] docs: liveupdate: Add documentation for VM and guest_memfd preservation ---
commit a930d2f58a9edc06bdcfd10fb383e97d7fe3996c
Author: Tarun Sahu <tarunsahu@google.com>
docs: liveupdate: Add documentation for VM and guest_memfd preservation
Add Documentation/liveupdate/vmm.rst detailing VMM requirements, scope,
ordering constraints, and kernel-doc inclusions for KVM VM and
guest_memfd preservation via Live Update Orchestrator (LUO).
> diff --git a/Documentation/liveupdate/vmm.rst b/Documentation/liveupdate/vmm.rst
> new file mode 100644
> index 0000000000000..b8e8fb2b63c21
> --- /dev/null
> +++ b/Documentation/liveupdate/vmm.rst
> @@ -0,0 +1,107 @@
> +.. SPDX-License-Identifier: GPL-2.0-or-later
> +
> +=============================
> +VM & Guest_Memfd Preservation
> +=============================
> +
> +.. kernel-doc:: virt/kvm/kvm_luo.c
> + :doc: KVM VM Preservation via LUO
> +
> +.. kernel-doc:: virt/kvm/guest_memfd_luo.c
> + :doc: Guest_Memfd Preservation via LUO
[Severity: Low]
Does this section title exactly match the DOC: title in
virt/kvm/guest_memfd_luo.c?
If the source file uses "Guestmemfd Preservation via LUO" without the
underscore, this directive will fail to find the documentation and generate a
Sphinx build warning.
[ ... ]
> +VM & Guest_Memfd Preservation ABI
> +=================================
> +
> +.. kernel-doc:: include/linux/kho/abi/kvm.h
> + :doc: DOC: guest_memfd Live Update ABI
[Severity: Low]
Does this section title match the actual title in include/linux/kho/abi/kvm.h?
If the source file uses "KVM and guest_memfd Live Update ABI", this mismatch
will cause empty sections in the generated documentation.
Also, should the literal "DOC: " prefix be omitted here? The directive
typically only expects the text that follows the prefix.
> +
> +.. kernel-doc:: include/linux/kho/abi/kvm.h
> + :internal:
> +
> +See Also
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260728121138.1103610-1-tarunsahu@google.com?part=9
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v4 10/11] KVM: selftests: Split ____vm_create() and add vm_create_from_fd()
2026-07-28 12:11 [PATCH v4 00/11] liveupdate: kvm: Guest_memfd preservation Tarun Sahu
` (8 preceding siblings ...)
2026-07-28 12:11 ` [PATCH v4 09/11] docs: liveupdate: Add documentation for VM and guest_memfd preservation Tarun Sahu
@ 2026-07-28 12:11 ` Tarun Sahu
2026-07-28 12:11 ` [PATCH v4 11/11] KVM: selftests: Add guest_memfd_preservation_test Tarun Sahu
10 siblings, 0 replies; 18+ messages in thread
From: Tarun Sahu @ 2026-07-28 12:11 UTC (permalink / raw)
To: ackerleytng, fuad.tabba, Andrew Morton, seanjc, dmatlack,
Shuah Khan, Jonathan Corbet, david, Tarun Sahu, Pasha Tatashin,
Pratyush Yadav, sagis, Paolo Bonzini, Mike Rapoport,
Alexander Graf
Cc: linux-kselftest, andre.przywara, michael.roth, linux-kernel,
linux-mm, will, vannapurve, maz, fvdl, kvm, oliver.upton, kvmarm,
alexandru.elisei, skhawaja, aneesh.kumar, linux-doc,
David Hildenbrand, yan.y.zhao, kexec, suzuki.poulose
Refactor ____vm_create() in the KVM selftest library to extract its
initialization steps into separate helpers vm_init_fields() and
vm_init_memory_properties(). Also introduce vm_create_from_fd() to
instantiate a struct kvm_vm from an existing VM file descriptor.
This allows test setups (such as guest_memfd preservation tests) to
reconstruct and initialize a struct kvm_vm when restoring preserved
VMs across live updates. No functional change is introduced for
existing tests.
Signed-off-by: Tarun Sahu <tarunsahu@google.com>
---
.../testing/selftests/kvm/include/kvm_util.h | 3 ++
tools/testing/selftests/kvm/lib/kvm_util.c | 49 ++++++++++++++++---
2 files changed, 44 insertions(+), 8 deletions(-)
diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h
index 04a910164a29..0626efa5a73b 100644
--- a/tools/testing/selftests/kvm/include/kvm_util.h
+++ b/tools/testing/selftests/kvm/include/kvm_util.h
@@ -471,6 +471,9 @@ const char *vm_guest_mode_string(u32 i);
void kvm_vm_free(struct kvm_vm *vmp);
void kvm_vm_restart(struct kvm_vm *vmp);
+void vm_init_fields(struct kvm_vm *vm, struct vm_shape shape);
+void vm_init_memory_properties(struct kvm_vm *vm);
+struct kvm_vm *vm_create_from_fd(int vm_fd, struct vm_shape shape);
void kvm_vm_release(struct kvm_vm *vmp);
void kvm_vm_elf_load(struct kvm_vm *vm, const char *filename);
int kvm_memfd_alloc(size_t size, bool hugepages);
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index 195f3fdae1e3..d3f98d9eb709 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -276,13 +276,8 @@ __weak void vm_populate_gva_bitmap(struct kvm_vm *vm)
(1ULL << (vm->va_bits - 1)) >> vm->page_shift);
}
-struct kvm_vm *____vm_create(struct vm_shape shape)
+void vm_init_fields(struct kvm_vm *vm, struct vm_shape shape)
{
- struct kvm_vm *vm;
-
- vm = calloc(1, sizeof(*vm));
- TEST_ASSERT(vm != NULL, "Insufficient Memory");
-
INIT_LIST_HEAD(&vm->vcpus);
vm->regions.gpa_tree = RB_ROOT;
vm->regions.hva_tree = RB_ROOT;
@@ -380,9 +375,10 @@ struct kvm_vm *____vm_create(struct vm_shape shape)
if (vm->pa_bits != 40)
vm->type = KVM_VM_TYPE_ARM_IPA_SIZE(vm->pa_bits);
#endif
+}
- vm_open(vm);
-
+void vm_init_memory_properties(struct kvm_vm *vm)
+{
/* Limit to VA-bit canonical virtual addresses. */
vm->vpages_valid = sparsebit_alloc();
vm_populate_gva_bitmap(vm);
@@ -392,10 +388,47 @@ struct kvm_vm *____vm_create(struct vm_shape shape)
/* Allocate and setup memory for guest. */
vm->vpages_mapped = sparsebit_alloc();
+}
+
+struct kvm_vm *____vm_create(struct vm_shape shape)
+{
+ struct kvm_vm *vm;
+
+ vm = calloc(1, sizeof(*vm));
+ TEST_ASSERT(vm != NULL, "Insufficient Memory");
+
+ vm_init_fields(vm, shape);
+
+ vm_open(vm);
+
+ vm_init_memory_properties(vm);
+
+ return vm;
+}
+
+struct kvm_vm *vm_create_from_fd(int vm_fd, struct vm_shape shape)
+{
+ struct kvm_vm *vm;
+
+ vm = calloc(1, sizeof(*vm));
+ TEST_ASSERT(vm != NULL, "Insufficient Memory");
+
+ vm_init_fields(vm, shape);
+
+ vm->kvm_fd = _open_kvm_dev_path_or_exit(O_RDWR);
+ vm->fd = vm_fd;
+
+ if (kvm_has_cap(KVM_CAP_BINARY_STATS_FD))
+ vm->stats.fd = vm_get_stats_fd(vm);
+ else
+ vm->stats.fd = -1;
+
+ vm_init_memory_properties(vm);
return vm;
}
+
static u64 vm_nr_pages_required(enum vm_guest_mode mode,
u32 nr_runnable_vcpus,
u64 extra_mem_pages)
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v4 11/11] KVM: selftests: Add guest_memfd_preservation_test
2026-07-28 12:11 [PATCH v4 00/11] liveupdate: kvm: Guest_memfd preservation Tarun Sahu
` (9 preceding siblings ...)
2026-07-28 12:11 ` [PATCH v4 10/11] KVM: selftests: Split ____vm_create() and add vm_create_from_fd() Tarun Sahu
@ 2026-07-28 12:11 ` Tarun Sahu
10 siblings, 0 replies; 18+ messages in thread
From: Tarun Sahu @ 2026-07-28 12:11 UTC (permalink / raw)
To: ackerleytng, fuad.tabba, Andrew Morton, seanjc, dmatlack,
Shuah Khan, Jonathan Corbet, david, Tarun Sahu, Pasha Tatashin,
Pratyush Yadav, sagis, Paolo Bonzini, Mike Rapoport,
Alexander Graf
Cc: linux-kselftest, andre.przywara, michael.roth, linux-kernel,
linux-mm, will, vannapurve, maz, fvdl, kvm, oliver.upton, kvmarm,
alexandru.elisei, skhawaja, aneesh.kumar, linux-doc,
David Hildenbrand, yan.y.zhao, kexec, suzuki.poulose
Add guest_memfd_preservation_test to verify that KVM VM state and guest
memory backed by guest_memfd are preserved across a kexec reboot via Live
Update Orchestrator (LUO).
This test includes multiple tests
- (A) Test to preserve non-guest_memfd, non-memfd which must fail (/dev/null)
- (B) Test to preserve guest_memfd with no INIT_SHARED flag which must fail
- (C) Test to allocate the guest_memfd memory after preservation because
preservation makes the guest_memfd frozen. Which must fail as well.
- (D) Test to verify guest_memfd with INIT_SHARED flag preservation across
kexec: Below is the details of stages in this test.
In Stage 1 (pre-kexec), create a VM with a guest_memfd memory slot, run guest
code to write a deterministic pattern into guest_memfd, and preserve both
the VM and guest_memfd file descriptors via LUO. This stage also tests
A, B and C. A, B and C do not require stage2 so they will finish in
stage 1 only.
In Stage 2 (post-kexec), retrieve the preserved VM and guest_memfd file
descriptors, reconstruct the VM using vm_create_from_fd(), re-map the
retrieved guest_memfd, and execute guest code to verify data integrity
against the original pattern.
Example usage:
$ ./guest_memfd_preservation_test --s 1
$ <kexec>
$ ./guest_memfd_preservation_test --s 2
Signed-off-by: Tarun Sahu <tarunsahu@google.com>
---
MAINTAINERS | 1 +
tools/testing/selftests/kvm/Makefile.kvm | 6 +-
.../kvm/guest_memfd_preservation_test.c | 357 ++++++++++++++++++
3 files changed, 363 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/kvm/guest_memfd_preservation_test.c
diff --git a/MAINTAINERS b/MAINTAINERS
index c032f9a0451b..df764e430d25 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14549,6 +14549,7 @@ L: kvm@vger.kernel.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux.git
F: Documentation/liveupdate/vmm.rst
+F: tools/testing/selftests/kvm/guest_memfd_preservation_test.c
F: virt/kvm/guest_memfd_luo.c
F: virt/kvm/kvm_luo.c
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index 4ace12606e93..02cfd7fc718e 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -165,6 +165,8 @@ TEST_GEN_PROGS_x86 += pre_fault_memory_test
# Compiled outputs used by test targets
TEST_GEN_PROGS_EXTENDED_x86 += x86/nx_huge_pages_test
+# Manual test that forks a persistent background daemon; skip auto CI run
+TEST_GEN_PROGS_EXTENDED_x86 += guest_memfd_preservation_test
TEST_GEN_PROGS_arm64 = $(TEST_GEN_PROGS_COMMON)
TEST_GEN_PROGS_arm64 += arm64/aarch32_id_regs
@@ -259,6 +261,7 @@ OVERRIDE_TARGETS = 1
# which causes the environment variable to override the makefile).
include ../lib.mk
include ../cgroup/lib/libcgroup.mk
+include ../liveupdate/lib/libliveupdate.mk
INSTALL_HDR_PATH = $(top_srcdir)/usr
LINUX_HDR_PATH = $(INSTALL_HDR_PATH)/include/
@@ -313,7 +316,8 @@ LIBKVM_S := $(filter %.S,$(LIBKVM))
LIBKVM_C_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM_C))
LIBKVM_S_OBJ := $(patsubst %.S, $(OUTPUT)/%.o, $(LIBKVM_S))
LIBKVM_STRING_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM_STRING))
-LIBKVM_OBJS = $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ) $(LIBKVM_STRING_OBJ) $(LIBCGROUP_O)
+LIBKVM_OBJS = $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ) $(LIBKVM_STRING_OBJ) \
+ $(LIBCGROUP_O) $(LIBLIVEUPDATE_O)
SPLIT_TEST_GEN_PROGS := $(patsubst %, $(OUTPUT)/%, $(SPLIT_TESTS))
SPLIT_TEST_GEN_OBJ := $(patsubst %, $(OUTPUT)/$(ARCH)/%.o, $(SPLIT_TESTS))
diff --git a/tools/testing/selftests/kvm/guest_memfd_preservation_test.c b/tools/testing/selftests/kvm/guest_memfd_preservation_test.c
new file mode 100644
index 000000000000..b790f2529837
--- /dev/null
+++ b/tools/testing/selftests/kvm/guest_memfd_preservation_test.c
@@ -0,0 +1,357 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026, Google LLC.
+ *
+ * Author: Tarun Sahu <tarunsahu@google.com>
+ *
+ * Test for VM and guest_memfd preservation across kexec (Live Update) via LUO.
+ *
+ * NOTE: This is a MANUAL test and is excluded from automated CI/testing
+ * frameworks because Stage 1 daemonizes into the background to pin resources
+ * and requires a human operator to manually trigger kexec before Stage 2
+ * is executed. Running Stage 1 automatically would leak the background daemon
+ * and cause CI runners to falsely interpret it as a passed test.
+ *
+ * Usage:
+ * Stage 1: ./guest_memfd_preservation_test --s 1
+ * Stage 2: ./guest_memfd_preservation_test --s 2
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <linux/sizes.h>
+#include <linux/falloc.h>
+
+#include "kvm_util.h"
+#include "processor.h"
+#include "test_util.h"
+#include "ucall_common.h"
+#include "../kselftest.h"
+#include "../kselftest_harness.h"
+
+#include <libliveupdate.h>
+
+#define TEST_PASSED(fmt, ...) ksft_print_msg(fmt, ##__VA_ARGS__)
+
+#define SESSION_NAME "gmem_vm_preservation_session"
+#define VM_TOKEN 0x1001
+#define GMEM_TOKEN 0x1002
+
+#define STATE_SESSION_NAME "gmem_preservation_state"
+#define STATE_TOKEN 0x999
+
+#define UNSHARED_GMEM_TOKEN 0x9001
+#define MMAP_ONLY_GMEM_TOKEN 0x9002
+#define NON_GMEM_TOKEN 0x9003
+#define FROZEN_GMEM_TOKEN 0x8888
+
+#define GPA SZ_4G
+#define GMEM_MEM_SLOT 1
+#define GMEM_SIZE (16ULL * 1024 * 1024)
+#define DATA_SIZE (5ULL * 1024 * 1024)
+
+static size_t page_size;
+
+/* Deterministic byte pattern generation based on offset */
+static inline uint8_t get_pattern_byte(size_t offset)
+{
+ return (uint8_t)(offset ^ 0x5A);
+}
+
+static void guest_code_phase1(uint64_t gpa)
+{
+ uint8_t *mem = (uint8_t *)gpa;
+
+ for (size_t i = 0; i < DATA_SIZE; i++)
+ mem[i] = get_pattern_byte(i);
+
+ GUEST_DONE();
+}
+
+static void guest_code_phase2(uint64_t gpa)
+{
+ uint8_t *mem = (uint8_t *)gpa;
+
+ for (size_t i = 0; i < DATA_SIZE; i++) {
+ uint8_t val = get_pattern_byte(i);
+
+ __GUEST_ASSERT(mem[i] == val,
+ "Data mismatch at offset %lu! Expected 0x%x, got 0x%x",
+ i, val, mem[i]);
+ }
+
+ GUEST_DONE();
+}
+
+static void setup_guest_memfd_region(struct kvm_vm *vm, int gmem_fd)
+{
+ vm_set_user_memory_region2(vm, GMEM_MEM_SLOT, KVM_MEM_GUEST_MEMFD, GPA, GMEM_SIZE, NULL,
+ gmem_fd, 0);
+
+ for (size_t i = 0; i < GMEM_SIZE; i += page_size)
+ virt_pg_map(vm, GPA + i, GPA + i);
+}
+
+static void test_preserve_disallowed_no_init_shared(int session_fd,
+ struct kvm_vm *vm)
+{
+ int unshared_fd, ret;
+
+ ksft_print_msg("[STAGE 1] TEST 1: Preserving guest_memfd without INIT_SHARED...\n");
+
+ unshared_fd = __vm_create_guest_memfd(vm, GMEM_SIZE, 0);
+ if (unshared_fd >= 0) {
+ ret = luo_session_preserve_fd(session_fd, unshared_fd, UNSHARED_GMEM_TOKEN);
+ TEST_ASSERT(ret < 0,
+ "Preservation without INIT_SHARED should fail");
+ close(unshared_fd);
+ }
+ TEST_PASSED("[STAGE 1] TEST 1: PASSED\n");
+}
+
+static void test_preserve_disallowed_mmap_only(int session_fd,
+ struct kvm_vm *vm)
+{
+ int mmap_only_fd, ret;
+
+ if (!(vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS) &
+ GUEST_MEMFD_FLAG_MMAP))
+ return;
+
+ ksft_print_msg("[STAGE 1] TEST 2: Preserving guest_memfd with MMAP only...\n");
+
+ mmap_only_fd = __vm_create_guest_memfd(vm, GMEM_SIZE,
+ GUEST_MEMFD_FLAG_MMAP);
+ if (mmap_only_fd >= 0) {
+ ret = luo_session_preserve_fd(session_fd, mmap_only_fd, MMAP_ONLY_GMEM_TOKEN);
+ TEST_ASSERT(ret < 0,
+ "Preserving guest_memfd with MMAP only should fail");
+ close(mmap_only_fd);
+ }
+ TEST_PASSED("[STAGE 1] TEST 2: PASSED\n");
+}
+
+static void test_preserve_disallowed_non_gmem(int session_fd)
+{
+ int null_fd, ret;
+
+ ksft_print_msg("[STAGE 1] TEST 3: Preserving non-guest_memfd FD (/dev/null)...\n");
+
+ null_fd = open("/dev/null", O_RDWR);
+ if (null_fd >= 0) {
+ ret = luo_session_preserve_fd(session_fd, null_fd, NON_GMEM_TOKEN);
+ TEST_ASSERT(ret < 0,
+ "Preserving /dev/null should fail");
+ close(null_fd);
+ }
+ TEST_PASSED("[STAGE 1] TEST 3: PASSED\n");
+}
+
+static void test_allocate_while_frozen(int luo_fd, struct kvm_vm *vm)
+{
+ int frozen_session, frozen_fd, ret;
+ uint64_t flags;
+ char *mem;
+
+ flags = GUEST_MEMFD_FLAG_MMAP | GUEST_MEMFD_FLAG_INIT_SHARED;
+ if ((vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS) & flags) != flags)
+ return;
+
+ ksft_print_msg("[STAGE 1] TEST 4: Operations on frozen guest_memfd...\n");
+
+ frozen_session = luo_create_session(luo_fd, "session_frozen_ops");
+ TEST_ASSERT(frozen_session >= 0, "Failed to create frozen test session");
+
+ frozen_fd = __vm_create_guest_memfd(vm, GMEM_SIZE, flags);
+ TEST_ASSERT(frozen_fd >= 0, "Failed to create guest_memfd for frozen test");
+
+ mem = kvm_mmap(GMEM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, frozen_fd);
+ mem[0] = get_pattern_byte(0);
+ kvm_munmap(mem, GMEM_SIZE);
+
+ ret = luo_session_preserve_fd(frozen_session, frozen_fd, FROZEN_GMEM_TOKEN);
+ TEST_ASSERT(ret == 0, "Failed to preserve guest_memfd for frozen test");
+
+ ret = fallocate(frozen_fd, FALLOC_FL_KEEP_SIZE, DATA_SIZE, page_size);
+ TEST_ASSERT(ret == -1 && errno == EPERM,
+ "fallocate on frozen guest_memfd failed ret=%d errno=%d",
+ ret, errno);
+
+ ret = fallocate(frozen_fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE,
+ 0, page_size);
+ TEST_ASSERT(ret == -1 && errno == EPERM,
+ "fallocate punch hole on frozen guest_memfd failed ret=%d errno=%d",
+ ret, errno);
+
+ mem = kvm_mmap(GMEM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, frozen_fd);
+
+ TEST_ASSERT_EQ(READ_ONCE(mem[0]), get_pattern_byte(0));
+ TEST_EXPECT_SIGBUS((void)READ_ONCE(mem[DATA_SIZE]));
+
+ kvm_munmap(mem, GMEM_SIZE);
+
+ luo_session_finish(frozen_session);
+ close(frozen_session);
+ close(frozen_fd);
+
+ TEST_PASSED("[STAGE 1] TEST 4: PASSED\n");
+}
+
+static void test_gmem_preservation_stage1(int session_fd, struct kvm_vm *vm,
+ struct kvm_vcpu *vcpu, int *gmem_fd)
+{
+ uint64_t flags = GUEST_MEMFD_FLAG_MMAP | GUEST_MEMFD_FLAG_INIT_SHARED;
+ int fd, ret;
+
+ ksft_print_msg("[STAGE 1] TEST 5: Preserving VM and guest_memfd...\n");
+
+ fd = vm_create_guest_memfd(vm, GMEM_SIZE, flags);
+ setup_guest_memfd_region(vm, fd);
+
+ vcpu_args_set(vcpu, 1, GPA);
+ vcpu_run(vcpu);
+ TEST_ASSERT_EQ(get_ucall(vcpu, NULL), UCALL_DONE);
+
+ ret = luo_session_preserve_fd(session_fd, vm->fd, VM_TOKEN);
+ TEST_ASSERT(ret == 0, "Failed to preserve VM file descriptor");
+
+ ret = luo_session_preserve_fd(session_fd, fd, GMEM_TOKEN);
+ TEST_ASSERT(ret == 0, "Failed to preserve guest_memfd file descriptor");
+
+ *gmem_fd = fd;
+}
+
+static void test_gmem_preservation_stage2(int retrieved_vm_fd,
+ int retrieved_gmem_fd)
+{
+ struct userspace_mem_region *slot0;
+ struct vm_shape shape = VM_SHAPE_DEFAULT;
+ u64 nr_pages = 2048; /* 8MB for slot0 pages */
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ int i;
+
+ ksft_print_msg("[STAGE 2] TEST 5 (cont): Verifying VM & guest_memfd data...\n");
+
+ vm = vm_create_from_fd(retrieved_vm_fd, shape);
+
+ vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, 0, 0,
+ nr_pages, 0);
+ kvm_vm_elf_load(vm, program_invocation_name);
+
+ for (i = 0; i < NR_MEM_REGIONS; i++)
+ vm->memslots[i] = 0;
+
+ slot0 = memslot2region(vm, 0);
+ ucall_init(vm, slot0->region.guest_phys_addr +
+ slot0->region.memory_size);
+
+ setup_guest_memfd_region(vm, retrieved_gmem_fd);
+
+ vcpu = vm_vcpu_add(vm, 0, guest_code_phase2);
+ kvm_arch_vm_finalize_vcpus(vm);
+
+ vcpu_args_set(vcpu, 1, GPA);
+
+ printf("Resuming / Running VM in Phase 2...\n");
+ vcpu_run(vcpu);
+ TEST_ASSERT_EQ(get_ucall(vcpu, NULL), UCALL_DONE);
+
+ /* kvm_vm_free will also close retrieved_vm_fd */
+ kvm_vm_free(vm);
+ TEST_PASSED("[STAGE 2] TEST 5: PASSED\n");
+}
+
+static void run_stage_1(int luo_fd)
+{
+ int gmem_fd, session_fd;
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+
+ ksft_print_msg("[STAGE 1] Starting pre-kexec setup...\n");
+
+ ksft_print_msg("[STAGE 1] Creating state file for next stage (2)...\n");
+ create_state_file(luo_fd, STATE_SESSION_NAME, STATE_TOKEN, 2);
+
+ vm = __vm_create_shape_with_one_vcpu(VM_SHAPE_DEFAULT, &vcpu, 1,
+ guest_code_phase1);
+
+ ksft_print_msg("[STAGE 1] Creating session '%s' and preserving FDs...\n",
+ SESSION_NAME);
+ session_fd = luo_create_session(luo_fd, SESSION_NAME);
+ TEST_ASSERT(session_fd >= 0, "Failed to create LUO session");
+
+ /* Run negative tests for disallowed preservation conditions */
+ test_preserve_disallowed_no_init_shared(session_fd, vm);
+ test_preserve_disallowed_mmap_only(session_fd, vm);
+ test_preserve_disallowed_non_gmem(session_fd);
+
+ /* Run operations on frozen guest_memfd test independently */
+ test_allocate_while_frozen(luo_fd, vm);
+
+ /* Run primary VM & guest_memfd preservation test */
+ test_gmem_preservation_stage1(session_fd, vm, vcpu, &gmem_fd);
+
+ printf("\n============================================================\n");
+ printf("Phase 1 Complete Successfully!\n");
+ printf("VM file and guest_memfd file have been preserved via LUO.\n");
+ printf("Tokens: VM_TOKEN=0x%x, GMEM_TOKEN=0x%x\n", VM_TOKEN, GMEM_TOKEN);
+ printf("Machine Size: %llu MB, Data Size: %llu MB\n", GMEM_SIZE / SZ_1M,
+ DATA_SIZE / SZ_1M);
+ printf("------------------------------------------------------------\n");
+
+ close(luo_fd);
+ daemonize_and_wait();
+}
+
+static void run_stage_2(int luo_fd, int state_session_fd)
+{
+ int retrieved_vm_fd, retrieved_gmem_fd, session_fd, stage;
+
+ ksft_print_msg("[STAGE 2] Starting post-kexec verification...\n");
+
+ restore_and_read_stage(state_session_fd, STATE_TOKEN, &stage);
+ if (stage != 2)
+ fail_exit("Expected stage 2, but state file contains %d", stage);
+
+ ksft_print_msg("[STAGE 2] Retrieving session '%s'...\n", SESSION_NAME);
+ session_fd = luo_retrieve_session(luo_fd, SESSION_NAME);
+ TEST_ASSERT(session_fd >= 0, "Failed to retrieve LUO session");
+
+ retrieved_vm_fd = luo_session_retrieve_fd(session_fd, VM_TOKEN);
+ TEST_ASSERT(retrieved_vm_fd >= 0, "Failed to retrieve VM file descriptor");
+
+ retrieved_gmem_fd = luo_session_retrieve_fd(session_fd, GMEM_TOKEN);
+ TEST_ASSERT(retrieved_gmem_fd >= 0, "Failed to retrieve guest_memfd file descriptor");
+
+ /* Run primary VM & guest_memfd post-kexec data verification */
+ test_gmem_preservation_stage2(retrieved_vm_fd, retrieved_gmem_fd);
+
+ printf("\nSUCCESS: Phase 2 Complete! All 5MB complex data verified intact!\n");
+
+ luo_session_finish(session_fd);
+ close(session_fd);
+
+ ksft_print_msg("[STAGE 2] Finalizing state session...\n");
+ if (luo_session_finish(state_session_fd) < 0)
+ fail_exit("luo_session_finish for state session");
+ close(state_session_fd);
+
+ close(retrieved_gmem_fd);
+}
+
+int main(int argc, char *argv[])
+{
+ TEST_REQUIRE(kvm_has_cap(KVM_CAP_GUEST_MEMFD));
+ page_size = getpagesize();
+
+ return luo_test(argc, argv, STATE_SESSION_NAME,
+ run_stage_1, run_stage_2);
+}
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread