* [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-08-10 22:58 ` Sean Christopherson
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; 34+ 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] 34+ 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-08-10 22:58 ` Sean Christopherson
2026-08-11 13:26 ` tarunsahu
0 siblings, 1 reply; 34+ messages in thread
From: Sean Christopherson @ 2026-08-10 22:58 UTC (permalink / raw)
To: Tarun Sahu
Cc: ackerleytng, fuad.tabba, Andrew Morton, dmatlack, Shuah Khan,
Jonathan Corbet, david, Pasha Tatashin, Pratyush Yadav, sagis,
Paolo Bonzini, Mike Rapoport, Alexander Graf, 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
On Tue, Jul 28, 2026, Tarun Sahu wrote:
> 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).
Why? Specifically, why do we need/want a separate Kconfig for guest_memfd
liveupdate versus KVM liveupdate? I can _maybe_ see a KVM-wide Kconfig, but
even that seems like a stretch given that (AFAIK) hypervisors are the one and
only use-case for liveupdate.
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH v4 01/11] liveupdate: Add LIVEUPDATE_GUEST_MEMFD config option
2026-08-10 22:58 ` Sean Christopherson
@ 2026-08-11 13:26 ` tarunsahu
2026-08-11 14:48 ` Sean Christopherson
0 siblings, 1 reply; 34+ messages in thread
From: tarunsahu @ 2026-08-11 13:26 UTC (permalink / raw)
To: Sean Christopherson
Cc: ackerleytng, fuad.tabba, Andrew Morton, dmatlack, Shuah Khan,
Jonathan Corbet, david, Pasha Tatashin, Pratyush Yadav, sagis,
Paolo Bonzini, Mike Rapoport, Alexander Graf, 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
Sean Christopherson <seanjc@google.com> writes:
> On Tue, Jul 28, 2026, Tarun Sahu wrote:
>> 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).
>
> Why? Specifically, why do we need/want a separate Kconfig for guest_memfd
> liveupdate versus KVM liveupdate? I can _maybe_ see a KVM-wide Kconfig, but
> even that seems like a stretch given that (AFAIK) hypervisors are the one and
> only use-case for liveupdate.
LIVEUPDATE is used in many subsystem. It is possible to enable
liveupdate and kvm without enabling KVM_GUEST_MEMFD. So GUEST_MEMFD
preservation must depend on CONFIG_LIVEUPDATE and KVM_GUEST_MEMFD both.
LIVEUPDATE_GUEST_MEMFD Kconfig is wrapper on both of them. It need not
to be visible in menu, So I can make it hidden. WDYT?
Also, KVM VM_FILE preservation and GUEST_MEMFD preservation both depends
on LIVEUPDATE_GUEST_MEMFD in this patch series. [PATCH 05/11]*
Which is wrong. So I will update KVM VM_FILE preservation to depend only
on CONFIG_LIVEUPDATE.
*https://lore.kernel.org/all/20260728121138.1103610-6-tarunsahu@google.com/#Z31virt:kvm:Makefile.kvm
~Tarun
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH v4 01/11] liveupdate: Add LIVEUPDATE_GUEST_MEMFD config option
2026-08-11 13:26 ` tarunsahu
@ 2026-08-11 14:48 ` Sean Christopherson
0 siblings, 0 replies; 34+ messages in thread
From: Sean Christopherson @ 2026-08-11 14:48 UTC (permalink / raw)
To: tarunsahu
Cc: ackerleytng, fuad.tabba, Andrew Morton, dmatlack, Shuah Khan,
Jonathan Corbet, david, Pasha Tatashin, Pratyush Yadav, sagis,
Paolo Bonzini, Mike Rapoport, Alexander Graf, 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
On Tue, Aug 11, 2026, tarunsahu@google.com wrote:
> Sean Christopherson <seanjc@google.com> writes:
>
> > On Tue, Jul 28, 2026, Tarun Sahu wrote:
> >> 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).
> >
> > Why? Specifically, why do we need/want a separate Kconfig for guest_memfd
> > liveupdate versus KVM liveupdate? I can _maybe_ see a KVM-wide Kconfig, but
> > even that seems like a stretch given that (AFAIK) hypervisors are the one and
> > only use-case for liveupdate.
>
> LIVEUPDATE is used in many subsystem. It is possible to enable
> liveupdate and kvm without enabling KVM_GUEST_MEMFD. So GUEST_MEMFD
> preservation must depend on CONFIG_LIVEUPDATE and KVM_GUEST_MEMFD both.
> LIVEUPDATE_GUEST_MEMFD Kconfig is wrapper on both of them. It need not
> to be visible in menu, So I can make it hidden. WDYT?
>
> Also, KVM VM_FILE preservation and GUEST_MEMFD preservation both depends
> on LIVEUPDATE_GUEST_MEMFD in this patch series. [PATCH 05/11]*
> Which is wrong. So I will update KVM VM_FILE preservation to depend only
> on CONFIG_LIVEUPDATE.
Or just handle it all in the makefile? E.g.
diff --git virt/kvm/Makefile.kvm virt/kvm/Makefile.kvm
index d047d4cf58c9..e6f098498795 100644
--- virt/kvm/Makefile.kvm
+++ virt/kvm/Makefile.kvm
@@ -13,3 +13,8 @@ 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
+
+ifdef CONFIG_LIVEUPDATE
+kvm-y += $(KVM)/kvm_luo.o
+kvm-$(CONFIG_KVM_GUEST_MEMFD) += $(KVM)/guest_memfd_luo.o
+endif
^ permalink raw reply related [flat|nested] 34+ 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-30 17:36 ` Ackerley Tng
2026-08-10 23:05 ` Sean Christopherson
2026-07-28 12:11 ` [PATCH v4 03/11] KVM: Export kvm_uevent_notify_vm_create() Tarun Sahu
` (8 subsequent siblings)
10 siblings, 2 replies; 34+ 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] 34+ messages in thread* Re: [PATCH v4 02/11] KVM: Introduce kvm_create_vm_file() helper
2026-07-28 12:11 ` [PATCH v4 02/11] KVM: Introduce kvm_create_vm_file() helper Tarun Sahu
@ 2026-07-30 17:36 ` Ackerley Tng
2026-08-10 10:14 ` tarunsahu
2026-08-10 23:05 ` Sean Christopherson
1 sibling, 1 reply; 34+ messages in thread
From: Ackerley Tng @ 2026-07-30 17:36 UTC (permalink / raw)
To: Tarun Sahu, fuad.tabba, Andrew Morton, seanjc, dmatlack,
Shuah Khan, Jonathan Corbet, david, 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
Tarun Sahu <tarunsahu@google.com> writes:
> - 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.
>
I think these should be 2 separate patches.
>
> [...snip...]
>
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH v4 02/11] KVM: Introduce kvm_create_vm_file() helper
2026-07-30 17:36 ` Ackerley Tng
@ 2026-08-10 10:14 ` tarunsahu
0 siblings, 0 replies; 34+ messages in thread
From: tarunsahu @ 2026-08-10 10:14 UTC (permalink / raw)
To: Ackerley Tng, fuad.tabba, Andrew Morton, seanjc, dmatlack,
Shuah Khan, Jonathan Corbet, david, 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
Ackerley Tng <ackerleytng@google.com> writes:
> Tarun Sahu <tarunsahu@google.com> writes:
>
>> - 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.
>>
>
> I think these should be 2 separate patches.
Okay, I will split them. Thanks
>
>>
>> [...snip...]
>>
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH v4 02/11] KVM: Introduce kvm_create_vm_file() helper
2026-07-28 12:11 ` [PATCH v4 02/11] KVM: Introduce kvm_create_vm_file() helper Tarun Sahu
2026-07-30 17:36 ` Ackerley Tng
@ 2026-08-10 23:05 ` Sean Christopherson
2026-08-11 13:27 ` tarunsahu
1 sibling, 1 reply; 34+ messages in thread
From: Sean Christopherson @ 2026-08-10 23:05 UTC (permalink / raw)
To: Tarun Sahu
Cc: ackerleytng, fuad.tabba, Andrew Morton, dmatlack, Shuah Khan,
Jonathan Corbet, david, Pasha Tatashin, Pratyush Yadav, sagis,
Paolo Bonzini, Mike Rapoport, Alexander Graf, 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
On Tue, Jul 28, 2026, Tarun Sahu wrote:
> - 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.
As Ackerley pointed out, changelogs that are literally a list pretty much always
needs to be split.
These also need to explain *why*. "providing a unified VM file creation API"
isn't sufficient justification. In general, all of these changelogs need to
provide more of the "why"; I can figure out what the patches are doing easily
enough, but in my speed read of the series, it's hard to figure out the "why".
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH v4 02/11] KVM: Introduce kvm_create_vm_file() helper
2026-08-10 23:05 ` Sean Christopherson
@ 2026-08-11 13:27 ` tarunsahu
0 siblings, 0 replies; 34+ messages in thread
From: tarunsahu @ 2026-08-11 13:27 UTC (permalink / raw)
To: Sean Christopherson
Cc: ackerleytng, fuad.tabba, Andrew Morton, dmatlack, Shuah Khan,
Jonathan Corbet, david, Pasha Tatashin, Pratyush Yadav, sagis,
Paolo Bonzini, Mike Rapoport, Alexander Graf, 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
Sean Christopherson <seanjc@google.com> writes:
> On Tue, Jul 28, 2026, Tarun Sahu wrote:
>> - 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.
>
> As Ackerley pointed out, changelogs that are literally a list pretty much always
> needs to be split.
>
> These also need to explain *why*. "providing a unified VM file creation API"
> isn't sufficient justification. In general, all of these changelogs need to
> provide more of the "why"; I can figure out what the patches are doing easily
> enough, but in my speed read of the series, it's hard to figure out the "why".
Okay, Will update them.
^ permalink raw reply [flat|nested] 34+ 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:11 ` [PATCH v4 04/11] KVM: Track weak reference to vm_file in struct kvm Tarun Sahu
` (7 subsequent siblings)
10 siblings, 0 replies; 34+ 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] 34+ 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-08-10 23:23 ` Sean Christopherson
2026-07-28 12:11 ` [PATCH v4 05/11] KVM: LUO: Support VM preservation across live updates Tarun Sahu
` (6 subsequent siblings)
10 siblings, 1 reply; 34+ 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] 34+ messages in thread* Re: [PATCH v4 04/11] KVM: Track weak reference to vm_file in struct kvm
2026-07-28 12:11 ` [PATCH v4 04/11] KVM: Track weak reference to vm_file in struct kvm Tarun Sahu
@ 2026-08-10 23:23 ` Sean Christopherson
0 siblings, 0 replies; 34+ messages in thread
From: Sean Christopherson @ 2026-08-10 23:23 UTC (permalink / raw)
To: Tarun Sahu
Cc: ackerleytng, fuad.tabba, Andrew Morton, dmatlack, Shuah Khan,
Jonathan Corbet, david, Pasha Tatashin, Pratyush Yadav, sagis,
Paolo Bonzini, Mike Rapoport, Alexander Graf, 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
On Tue, Jul 28, 2026, Tarun Sahu wrote:
> - Add a weak reference 'vm_file' in struct kvm under
"vm_file" is effectively already claimed by vm_area_struct.vm_file. *If* we go
this route, I would very strongly prefer to take a dependency on "vfio: Use
file-based reference counting for KVM"[*]. I'll poke that thread, we really
should land that one particular patch long before the s390-is-arm64 insanity.
Empashish on "if" because this all feels backwards. It's not at all clear to
me why liveupdate goes directly to guest_memfd, but then requires a token associated
with the VM.
https://lore.kernel.org/all/20260731130902.654679-2-seiden@linux.ibm.com
^ permalink raw reply [flat|nested] 34+ 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-08-10 23:42 ` Sean Christopherson
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; 34+ 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] 34+ 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-08-10 23:42 ` Sean Christopherson
2026-08-11 11:31 ` Pratyush Yadav
0 siblings, 1 reply; 34+ messages in thread
From: Sean Christopherson @ 2026-08-10 23:42 UTC (permalink / raw)
To: Tarun Sahu
Cc: ackerleytng, fuad.tabba, Andrew Morton, dmatlack, Shuah Khan,
Jonathan Corbet, david, Pasha Tatashin, Pratyush Yadav, sagis,
Paolo Bonzini, Mike Rapoport, Alexander Graf, 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
On Tue, Jul 28, 2026, Tarun Sahu wrote:
> 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.
Why?
> 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.
Uh, why guest_memfd? As much as I want to push guest_memfd adoption, it seems
guest_memfd should be the _last_ thing we support, not the first. As evidenced
by the last two decades, it's very doable to have KVM VMs without guest_memfd,
but it's rather hard to have VMs without vCPUs.
> 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
NAK on taking changes through a different tree. This is KVM code, period.
In general, I'm skeptical of the dedicated MAINTAINERS entry. It's extremely
difficult to tell since this series is little more than a skeleton (either that
or liveupdate is way simpler that I was expecting), but I suspect that maintaining
liveupdate for KVM (or for any subsystem) will require more subsystem-specific
knowledge than liveupdate knowledge.
E.g. the LUO APIs seem pretty straightforward; I assume the bulk of the complexity
is going to be in knowing what to save/restore, and how, which is much more about
KVM than it is about liveupdate.
> +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.
IMO, this is not helpful documentation. Anyone with passing knowledge of LUO
already knows the above. What people like me don't know are the exact details
of how preserving a KVM guest is expected to work.
> + * 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
So... what's the plan? Bluntly, this series isn't going anywhere without a
clear plan of how all of this is going to fit together.
> +static int kvm_luo_preserve(struct liveupdate_file_op_args *args)
> +{
> + struct kvm *kvm = args->file->private_data;
> + struct kvm_luo_ser *ser;
I *really* dislike the "ser" nomenclature. The abbreviation isn't common, and
IMO it's not intuitive. This also needs to be more specific, becuase I'm guessing
we'll end up with more than one "kvm" LUO structure.
Maybe something like kvm_vm_luo_state?
> +
> + 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
This needs to be properly supported with arch callbacks. And per-arch enabling
needs to be done in separate patches.
> +
> + 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);
KVM generally uses __va() and __pa(), is there a reason to diverge?
^ permalink raw reply [flat|nested] 34+ messages in thread* Re: [PATCH v4 05/11] KVM: LUO: Support VM preservation across live updates
2026-08-10 23:42 ` Sean Christopherson
@ 2026-08-11 11:31 ` Pratyush Yadav
2026-08-11 14:05 ` Sean Christopherson
0 siblings, 1 reply; 34+ messages in thread
From: Pratyush Yadav @ 2026-08-11 11:31 UTC (permalink / raw)
To: Sean Christopherson
Cc: Tarun Sahu, ackerleytng, fuad.tabba, Andrew Morton, dmatlack,
Shuah Khan, Jonathan Corbet, david, Pasha Tatashin,
Pratyush Yadav, sagis, Paolo Bonzini, Mike Rapoport,
Alexander Graf, 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
On Mon, Aug 10 2026, Sean Christopherson wrote:
> On Tue, Jul 28, 2026, Tarun Sahu wrote:
>> 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.
>
> Why?
>
>> 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.
>
> Uh, why guest_memfd? As much as I want to push guest_memfd adoption, it seems
> guest_memfd should be the _last_ thing we support, not the first. As evidenced
> by the last two decades, it's very doable to have KVM VMs without guest_memfd,
> but it's rather hard to have VMs without vCPUs.
You _can_ preserve vCPUs today using KVM_{GET,SET}_REGS, they just won't
run in the background during the reboot. This series can save you from
dumping VM memory to disk if it is backed by guest_memfd.
>
>> 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
>
> NAK on taking changes through a different tree. This is KVM code, period.
>
> In general, I'm skeptical of the dedicated MAINTAINERS entry. It's extremely
> difficult to tell since this series is little more than a skeleton (either that
> or liveupdate is way simpler that I was expecting), but I suspect that maintaining
It's a bit of both. This series of course doesn't support everything
that guest_memfd can. At the same time, I also keep being (pleasantly)
surprised at preservation being relatively simple. For example, the code
to preserve a shmem file (via memfd) is roughly 600 lines, a big chunk
of which is comments. The code of course has some limitations, but it is
good enough for use in production.
> liveupdate for KVM (or for any subsystem) will require more subsystem-specific
> knowledge than liveupdate knowledge.
>
> E.g. the LUO APIs seem pretty straightforward; I assume the bulk of the complexity
> is going to be in knowing what to save/restore, and how, which is much more about
> KVM than it is about liveupdate.
I think it is fine if you want to take these changes through the KVM
tree, but I would like live update maintainers to be listed as reviewers
at least.
For one, we care about ABI breakages and versioning. The serialized
state is a part of live update ABI and changes to it should be ACKed by
us. For another, how the file handlers interact with their dependencies
can affect the behaviour that VMMs observe. Those changes should also
pass by some live update eyes.
So I think we should have a separate entry for KVM LIVE UPDATE that
lists the live update maintainers (or perhaps only kexec@) as a
reviewer. The patches can still flow through KVM tree but we'd get a
chance to ACK/NACK them.
Of course this all can evolve later as we see fit but I think this is a
good starting point.
>
>> +F: virt/kvm/kvm_luo.c
>> +
>> KVM PARAVIRT (KVM/paravirt)
>> M: Paolo Bonzini <pbonzini@redhat.com>
>> R: Vitaly Kuznetsov <vkuznets@redhat.com>
[...]
--
Regards,
Pratyush Yadav
^ permalink raw reply [flat|nested] 34+ messages in thread* Re: [PATCH v4 05/11] KVM: LUO: Support VM preservation across live updates
2026-08-11 11:31 ` Pratyush Yadav
@ 2026-08-11 14:05 ` Sean Christopherson
0 siblings, 0 replies; 34+ messages in thread
From: Sean Christopherson @ 2026-08-11 14:05 UTC (permalink / raw)
To: Pratyush Yadav
Cc: Tarun Sahu, ackerleytng, fuad.tabba, Andrew Morton, dmatlack,
Shuah Khan, Jonathan Corbet, david, Pasha Tatashin, sagis,
Paolo Bonzini, Mike Rapoport, Alexander Graf, 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
On Tue, Aug 11, 2026, Pratyush Yadav wrote:
> On Mon, Aug 10 2026, Sean Christopherson wrote:
>
> > On Tue, Jul 28, 2026, Tarun Sahu wrote:
> >> 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.
> >
> > Why?
> >
> >> 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.
> >
> > Uh, why guest_memfd? As much as I want to push guest_memfd adoption, it seems
> > guest_memfd should be the _last_ thing we support, not the first. As evidenced
> > by the last two decades, it's very doable to have KVM VMs without guest_memfd,
> > but it's rather hard to have VMs without vCPUs.
>
> You _can_ preserve vCPUs today using KVM_{GET,SET}_REGS, they just won't
> run in the background during the reboot.
What about x86 CoCo VMs? Which are quite literally _the_ reason guest_memfd was
created in the first place.
> This series can save you from dumping VM memory to disk if it is backed by
> guest_memfd.
Or to word it another way, one _can_ save guest_memfd, it's just slower. My
point is that this series needs to provide a _lot_ more information about the
bigger KVM picture. For those of us that are on the very fringes of live update,
it's practically impossible to review because, to us, it seems very arbitrary.
The part that's especially confusing is the saving of the VM type. That comes
straight from userspace, so it's super bizarre to automatically save/restore that,
but nothing else.
> >> +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
> >
> > NAK on taking changes through a different tree. This is KVM code, period.
> >
> > In general, I'm skeptical of the dedicated MAINTAINERS entry. It's extremely
> > difficult to tell since this series is little more than a skeleton (either that
> > or liveupdate is way simpler that I was expecting), but I suspect that maintaining
...
> > E.g. the LUO APIs seem pretty straightforward; I assume the bulk of the complexity
> > is going to be in knowing what to save/restore, and how, which is much more about
> > KVM than it is about liveupdate.
>
> I think it is fine if you want to take these changes through the KVM
> tree, but I would like live update maintainers to be listed as reviewers
> at least.
Why not simply add a file pattern match to the LIVE UPDATE entry?
diff --git MAINTAINERS MAINTAINERS
index 8014b9f8253e..2eb57b22c37f 100644
--- MAINTAINERS
+++ MAINTAINERS
@@ -15052,8 +15052,8 @@ F: include/linux/liveupdate.h
F: include/uapi/linux/liveupdate.h
F: kernel/liveupdate/
F: lib/tests/liveupdate.c
-F: mm/memfd_luo.c
F: tools/testing/selftests/liveupdate/
+N: [^a-z]luo
LLC (802.2)
L: netdev@vger.kernel.org
> At the same time, I also keep being (pleasantly)
> surprised at preservation being relatively simple. For example, the code
> to preserve a shmem file (via memfd) is roughly 600 lines, a big chunk
> of which is comments. The code of course has some limitations, but it is
> good enough for use in production.
>
> For one, we care about ABI breakages and versioning.
Which is amusing to me because that implies KVM does not, and I would hazard to
guess that KVM has the biggest ABI surface of any subsystem in the kernel by a
country mile (though I'm probably wildly underestimating the effective ABI surface
of filesystems).
> The serialized state is a part of live update ABI and changes to it should be
> ACKed by us.
Meh, "Don't break userspace" is a universal rule in the kernel, I genuinely don't
see why liveupdate needs special treatment.
> For another, how the file handlers interact with their dependencies can
> affect the behaviour that VMMs observe. Those changes should also pass by
> some live update eyes.
Perhaps in the short term, but IMO, that's not a winning strategy in the long
term. From my perspective, that like saying the PAGE CACHE maintainers should
review every usage of the filemap APIs, because how the APIs are used impacts
the page cache and affects userspace-visible behavior. There are myriad analogies
like that throughout the kernel.
Yes, liveupdate is new and shiny, but IMO for it to be successful and maintainable,
it needs to be treated like any other core infrastructure in the kernel, not a
special snowflake whose details are known only by a handful of people. Because
I think it's likely liveupdate goes one of two ways: either liveupdate becomes a
very niche thing that is used sparingly throughout the kernel, or it becomes a
broadly used feature that is supported by many filesystems and subsystems.
If liveupdate is relegated to niche status, then it probably isn't going to see
a significant amount of ongoing development, at which point the folks working on
liveupdate will naturally migrate to other projects, and maintenance will largely
be left to subsystem maintainers.
If liveupdate is broadly used, then having a single group of people maintain
every subsystem's usage won't scale, and maintenance will again largely fall on
the shoulder of subsystem maintainers. Which is totally fine and working as
intended, because that's exactly what subystem maintainers are signing up for
by merging support for liveupdate.
^ permalink raw reply [flat|nested] 34+ 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-30 18:12 ` Ackerley Tng
2026-07-28 12:11 ` [PATCH v4 07/11] KVM: guest_memfd: Add support for freezing mappings Tarun Sahu
` (4 subsequent siblings)
10 siblings, 1 reply; 34+ 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] 34+ messages in thread* Re: [PATCH v4 06/11] KVM: guest_memfd: Move internal definitions to internal header
2026-07-28 12:11 ` [PATCH v4 06/11] KVM: guest_memfd: Move internal definitions to internal header Tarun Sahu
@ 2026-07-30 18:12 ` Ackerley Tng
2026-08-11 10:31 ` Pratyush Yadav
0 siblings, 1 reply; 34+ messages in thread
From: Ackerley Tng @ 2026-07-30 18:12 UTC (permalink / raw)
To: Tarun Sahu, fuad.tabba, Andrew Morton, seanjc, dmatlack,
Shuah Khan, Jonathan Corbet, david, 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
Tarun Sahu <tarunsahu@google.com> writes:
> 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.
>
This is done basically to support a new virt/kvm/guest_memfd_luo.c
file.
Would like to know what Sean thinks of this! I'd like to know for future
guest_memfd work too.
> 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 [flat|nested] 34+ messages in thread* Re: [PATCH v4 06/11] KVM: guest_memfd: Move internal definitions to internal header
2026-07-30 18:12 ` Ackerley Tng
@ 2026-08-11 10:31 ` Pratyush Yadav
0 siblings, 0 replies; 34+ messages in thread
From: Pratyush Yadav @ 2026-08-11 10:31 UTC (permalink / raw)
To: Ackerley Tng
Cc: Tarun Sahu, fuad.tabba, Andrew Morton, seanjc, dmatlack,
Shuah Khan, Jonathan Corbet, david, Pasha Tatashin,
Pratyush Yadav, sagis, Paolo Bonzini, Mike Rapoport,
Alexander Graf, 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
On Thu, Jul 30 2026, Ackerley Tng wrote:
> Tarun Sahu <tarunsahu@google.com> writes:
>
>> 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.
>>
>
> This is done basically to support a new virt/kvm/guest_memfd_luo.c
> file.
My $0.02 on this: I think it is useful to split out the live update code
into a separate file. It makes for easier reviews, and puts a clear
boundary between core and live update. We have done it for memfd/shmem
for example (see mm/memfd_luo.c) and for other live update series in
flight like PCI or IOMMU.
>
> Would like to know what Sean thinks of this! I'd like to know for future
> guest_memfd work too.
>
>> 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>
[...]
--
Regards,
Pratyush Yadav
^ permalink raw reply [flat|nested] 34+ 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-30 17:46 ` Ackerley Tng
` (2 more replies)
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, 3 replies; 34+ 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] 34+ 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-30 17:46 ` Ackerley Tng
2026-08-10 13:15 ` tarunsahu
2026-07-30 18:12 ` Ackerley Tng
2026-08-10 23:44 ` Sean Christopherson
2 siblings, 1 reply; 34+ messages in thread
From: Ackerley Tng @ 2026-07-30 17:46 UTC (permalink / raw)
To: Tarun Sahu, fuad.tabba, Andrew Morton, seanjc, dmatlack,
Shuah Khan, Jonathan Corbet, david, 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
Tarun Sahu <tarunsahu@google.com> writes:
> 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.
>
I wonder if everything to do with freezing should be gated behind
CONFIG_LIVEUPDATE_GUEST_MEMFD. What do people think?
> Signed-off-by: Tarun Sahu <tarunsahu@google.com>
>
> [...snip...]
>
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH v4 07/11] KVM: guest_memfd: Add support for freezing mappings
2026-07-30 17:46 ` Ackerley Tng
@ 2026-08-10 13:15 ` tarunsahu
0 siblings, 0 replies; 34+ messages in thread
From: tarunsahu @ 2026-08-10 13:15 UTC (permalink / raw)
To: Ackerley Tng, fuad.tabba, Andrew Morton, seanjc, dmatlack,
Shuah Khan, Jonathan Corbet, david, 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
Ackerley Tng <ackerleytng@google.com> writes:
> Tarun Sahu <tarunsahu@google.com> writes:
>
>> 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.
>>
>
> I wonder if everything to do with freezing should be gated behind
> CONFIG_LIVEUPDATE_GUEST_MEMFD. What do people think?
Right now, Only liveupdate is the usecase. There was a discussion
around the future usecase when IOMMU FD can use few pages from
guest_memfd and will have to prevent punch_hole operation. But
that will also avoid new faults. So freezing is not the solution here.
A SEAL type solution like in memfd can be explored later. But this
is very far in future so Plenty of time to discuss more ideas.
On gating the freeze across CONFIG_LIVEUPDATE_GUEST_MEMFD sounds good
Idea. As in future, there is a plan to remove the freeze at all from
the preservation path. Before LIVEUPDATE, there was no usecase and in
future, that usecase will also be gone. So gating it against the config
makes sense.
Would love to know what do people think?
~Tarun
>
>> Signed-off-by: Tarun Sahu <tarunsahu@google.com>
>>
>> [...snip...]
>>
^ permalink raw reply [flat|nested] 34+ 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-30 17:46 ` Ackerley Tng
@ 2026-07-30 18:12 ` Ackerley Tng
2026-08-10 13:08 ` tarunsahu
2026-08-10 23:44 ` Sean Christopherson
2 siblings, 1 reply; 34+ messages in thread
From: Ackerley Tng @ 2026-07-30 18:12 UTC (permalink / raw)
To: Tarun Sahu, fuad.tabba, Andrew Morton, seanjc, dmatlack,
Shuah Khan, Jonathan Corbet, david, 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
Tarun Sahu <tarunsahu@google.com> writes:
>
> [...snip...]
>
> @@ -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;
> + }
>
Continuing from [1], this guard is actually meant for PUNCH_HOLE... I
wonder if there could be a deeper refactoring to check when actually
punching a hole, basically the equivalent place as kvm_gmem_get_folio()
in the flow of fallocating.
Will srcu_read_lock() be taken in a nested way for the fallocate case?
It can be taken in a nested way with some intricacies, but maybe if the
freeze check can be done just as the folio is about to be removed (as
described in the above paragraph), it would avoid this nested locking
here.
[1] https://lore.kernel.org/all/2vxzzf0kl3dt.fsf@kernel.org/
> 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;
> }
>
>
> [...snip...]
>
^ permalink raw reply [flat|nested] 34+ messages in thread* Re: [PATCH v4 07/11] KVM: guest_memfd: Add support for freezing mappings
2026-07-30 18:12 ` Ackerley Tng
@ 2026-08-10 13:08 ` tarunsahu
0 siblings, 0 replies; 34+ messages in thread
From: tarunsahu @ 2026-08-10 13:08 UTC (permalink / raw)
To: Ackerley Tng, fuad.tabba, Andrew Morton, seanjc, dmatlack,
Shuah Khan, Jonathan Corbet, david, 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
Ackerley Tng <ackerleytng@google.com> writes:
> Tarun Sahu <tarunsahu@google.com> writes:
>
>>
>> [...snip...]
>>
>> @@ -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;
>> + }
>>
>
> Continuing from [1], this guard is actually meant for PUNCH_HOLE... I
> wonder if there could be a deeper refactoring to check when actually
> punching a hole, basically the equivalent place as kvm_gmem_get_folio()
> in the flow of fallocating.
>
> Will srcu_read_lock() be taken in a nested way for the fallocate case?
> It can be taken in a nested way with some intricacies, but maybe if the
> freeze check can be done just as the folio is about to be removed (as
> described in the above paragraph), it would avoid this nested locking
> here.
>
> [1] https://lore.kernel.org/all/2vxzzf0kl3dt.fsf@kernel.org/
Instead of avoiding on remove folio, We can just add the freeze check
across the PUNCH_HOLE, So there will be no nested lock anymore. Nested
lock is just with fallocate populate -> kvm_gmem_get_pfn. So this nested
ness will be removed with this update.
~Tarun
>
>> 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;
>> }
>>
>>
>> [...snip...]
>>
^ permalink raw reply [flat|nested] 34+ 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-30 17:46 ` Ackerley Tng
2026-07-30 18:12 ` Ackerley Tng
@ 2026-08-10 23:44 ` Sean Christopherson
2 siblings, 0 replies; 34+ messages in thread
From: Sean Christopherson @ 2026-08-10 23:44 UTC (permalink / raw)
To: Tarun Sahu
Cc: ackerleytng, fuad.tabba, Andrew Morton, dmatlack, Shuah Khan,
Jonathan Corbet, david, Pasha Tatashin, Pratyush Yadav, sagis,
Paolo Bonzini, Mike Rapoport, Alexander Graf, 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
On Tue, Jul 28, 2026, Tarun Sahu wrote:
> +/**
> + * 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.
This argument doesn't hold up given that each kvm structure has two SRCU structs.
^ permalink raw reply [flat|nested] 34+ 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-30 18:16 ` Ackerley Tng
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; 34+ 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] 34+ 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-30 18:16 ` Ackerley Tng
2026-08-10 13:20 ` tarunsahu
0 siblings, 1 reply; 34+ messages in thread
From: Ackerley Tng @ 2026-07-30 18:16 UTC (permalink / raw)
To: Tarun Sahu, fuad.tabba, Andrew Morton, seanjc, dmatlack,
Shuah Khan, Jonathan Corbet, david, 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
Tarun Sahu <tarunsahu@google.com> writes:
>
> [...snip...]
>
> +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;
> +
The above check can probably be removed. I think the core thing about
this series is "we can preserve any memory that doesn't have any
encryption state that goes along with it", and so the check for
kvm_arch_has_private_mem() below would be sufficient to gate that.
> + /*
> + * 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;
> +
For now, has_private_mem == has some encryption state that we can't
preserve yet.
This also gates off KVM_X86_SW_PROTECTED_VM, but I guess that is
intended, since when work is done for preserving encryption state, we
can then test using KVM_X86_SW_PROTECTED_VM, and only then, remove this
check?
> + if (mapping_large_folio_support(inode->i_mapping))
> + return false;
> +
> + return true;
> +}
> +
>
> [...snip...]
>
^ permalink raw reply [flat|nested] 34+ messages in thread* Re: [PATCH v4 08/11] KVM: guest_memfd: Add support for preservation via LUO
2026-07-30 18:16 ` Ackerley Tng
@ 2026-08-10 13:20 ` tarunsahu
0 siblings, 0 replies; 34+ messages in thread
From: tarunsahu @ 2026-08-10 13:20 UTC (permalink / raw)
To: Ackerley Tng, fuad.tabba, Andrew Morton, seanjc, dmatlack,
Shuah Khan, Jonathan Corbet, david, 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
Ackerley Tng <ackerleytng@google.com> writes:
> Tarun Sahu <tarunsahu@google.com> writes:
>
>>
>> [...snip...]
>>
>> +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;
>> +
>
> The above check can probably be removed. I think the core thing about
> this series is "we can preserve any memory that doesn't have any
> encryption state that goes along with it", and so the check for
> kvm_arch_has_private_mem() below would be sufficient to gate that.
Sounds Good. Will remove it in next version. I will rephrase the
Line: "Support only fully shared guest_memfd" => "Support only
guest_memfd that does not support private pages and also can not be
converted to private"
>
>> + /*
>> + * 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;
>> +
>
> For now, has_private_mem == has some encryption state that we can't
> preserve yet.
>
> This also gates off KVM_X86_SW_PROTECTED_VM, but I guess that is
> intended, since when work is done for preserving encryption state, we
> can then test using KVM_X86_SW_PROTECTED_VM, and only then, remove this
> check?
Yes, It will be enabled when private memory preservation will be supported.
>
>> + if (mapping_large_folio_support(inode->i_mapping))
>> + return false;
>> +
>> + return true;
>> +}
>> +
>>
>> [...snip...]
>>
^ permalink raw reply [flat|nested] 34+ 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: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, 0 replies; 34+ 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] 34+ 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; 34+ 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] 34+ 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
2026-07-30 18:18 ` Ackerley Tng
10 siblings, 1 reply; 34+ 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] 34+ messages in thread* Re: [PATCH v4 11/11] KVM: selftests: Add guest_memfd_preservation_test
2026-07-28 12:11 ` [PATCH v4 11/11] KVM: selftests: Add guest_memfd_preservation_test Tarun Sahu
@ 2026-07-30 18:18 ` Ackerley Tng
2026-08-10 13:22 ` tarunsahu
0 siblings, 1 reply; 34+ messages in thread
From: Ackerley Tng @ 2026-07-30 18:18 UTC (permalink / raw)
To: Tarun Sahu, fuad.tabba, Andrew Morton, seanjc, dmatlack,
Shuah Khan, Jonathan Corbet, david, 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
Tarun Sahu <tarunsahu@google.com> writes:
> 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.
>
I think we should split this into multiple patches, where each patch
introduces 1 test.
>
> [...snip...]
>
> +++ 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.
> + *
Usually there aren't many printfs in tests, but since this is a MANUAL
test, maybe that's ok?
Would like to know what Sean thinks of this, and printfs in general.
>
> [...snip...]
>
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH v4 11/11] KVM: selftests: Add guest_memfd_preservation_test
2026-07-30 18:18 ` Ackerley Tng
@ 2026-08-10 13:22 ` tarunsahu
0 siblings, 0 replies; 34+ messages in thread
From: tarunsahu @ 2026-08-10 13:22 UTC (permalink / raw)
To: Ackerley Tng, fuad.tabba, Andrew Morton, seanjc, dmatlack,
Shuah Khan, Jonathan Corbet, david, 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
Ackerley Tng <ackerleytng@google.com> writes:
> Tarun Sahu <tarunsahu@google.com> writes:
>
>> 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.
>>
>
> I think we should split this into multiple patches, where each patch
> introduces 1 test.
Got it. I will split them. I thought that this test already spawn a VM
and other test can just reuse the same VM.
>
>>
>> [...snip...]
>>
>> +++ 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.
>> + *
>
> Usually there aren't many printfs in tests, but since this is a MANUAL
> test, maybe that's ok?
>
> Would like to know what Sean thinks of this, and printfs in general.
>
>>
>> [...snip...]
>>
^ permalink raw reply [flat|nested] 34+ messages in thread