Skip to contentSkip to conversation

liveupdate: kvm: Guest_memfd preservation

RFC PATCH In review Tarun Sahu 3 participants 16 messages May 18 – Jun 7

Subsystems (6)
lore
Share & tools
lore

Tarun Sahu

author
#0 lore raw

cover liveupdate: kvm: Guest_memfd preservation

Hello,

I am proposing this series as RFC, to initiate the discussion for supporting the guest_memfd preservation. This will setup basic arhitecture for VM preservation during liveupdate. This Cover letter has three sections (please feel free to skip the on you already know):

A. Guest_memfd introduction:
To make the audience familiar with guest_memfd
B. Liveupdate introduction:
To make the audience familiar with liveupdate
C. Actual Implementation Design and questions.

**GUEST MEMFD INTRODUCTION**

Initially, guest_memfd was created to support guest private memory in confidential computing VMs (CoCo VMs). It was designed so that whenever a guest wants to grant the host access to private memory, a series of calls occurs: from the guest to KVM, KVM to the host userspace, host userspace back to KVM, and finally a new page fault maps the memory into a separate shared address space. Conversely, if the guest transitions the memory back to private, the subsequent fault is handled by guest_memfd. (Dual Mapping Architecture). In such a VM, all guest memory is initially shared. On the fly, the guest may request to change pages to private; the metadata indicating which parts of memory are private is stored in an xarray inside struct kvm (mem_attr_array). This array serves as the source of truth for the fault mechanism, determining whether a mapping should be created from host-userspace-mapped pages or directly from the guest_memfd file. For private memory, Fault also calls architecture-specific function to set up private hardware access (e.g., on SEV-SNP or TDX). This type of guest_memfd is fully-private where shared mapping comes from userspace mapped address space.

Subsequently, support was added to allow the entire guest memory to be backed by guest_memfd. This led to the implementation of the MMAP and INIT_SHARED flags for the guest_memfd inode. When KVM_CREATE_GUEST_MEMFD is called with these flags, the guest_memfd becomes mmap-able by host userspace. The INIT_SHARED flag is used to make the guest_memfd completely shared between the host and the guest. Consequently, page faults from both host userspace and the guest resolve to the same guest_memfd page cache. However, under this configuration, marking a portion of this memory as private is not possible. This type of guest_memfd is fully-shared.

If guest_memfd is created with INIT_SHARED without MMAP, the host can never access the guest_memfd. But the memory is still considered shared.

Hence, At this point, Only use-case of guest_memfd is either fully-shared or fully-private.

There is ongoing work to make shared and private mapping in-place backed by guest_memfd. [1] There is also ongoing work to back guest_memfd by hugetlb pages. [2]

**LIVEUPDATE INTRODUCTION (LIVEUPDATE ORCHESTRATOR - LUO)**

Livepdate support was added in kernel to update the host kernel by minimizing the downtime to minimal. This is generally achieved by preserving the current state of the system and retrieve after boot to resume from where we left it.

Any subsystem that wants to preserve themselves, register their handler with liveupdate system. This handler includes calls to the following

*can_preserve (file)*:
This tells the luo system about the eligibility of the file. When preserve ioctl is called, it first loop through all the file handlers and call can_preserve, the one which return true, luo uses this file handler fh->preserve call to preserve the file.

*preserve(file)*:
This actually preserves the file.

*unpreserve(file)*:
This unpreserve the file incase userspace want to go back.

*retrieve(file)*:
On new kernel boot, this function retrieves the file.

*finish(file)*:
When userspace decides that all the files in the liveupdate session has been retrieved, it can trigger this to do final work of cleaning up.

LUO preserve its memory using KHO (kexec-handover). All these APIs will be implemented using KHO calls.

**GUEST MEMFD PRESERVATION**

This patch sets up the basic infrastructure to preserve the guest_memfd. Currently this supports only fully-shared, pre-faulted guest_memfd (INIT_SHARED) backed by PAGE_SIZE pages.

It registers a new LUO file handler for guest_memfd file to serialize and deserialize guest memory. This allows preserving guest memory backed by guest_memfd across updates, ensuring that guest instances can be resumed seamlessly without losing their memory contents.

The preservation call is straight forward. It walks through the page cache, serialize the folios and preserve them.

On the retrieval path:
Currently, creating a guest_memfd requires an associated struct kvm (derived from vm_file / vm_fd). Since there is no direct way to pass a VM file descriptor via the LUO API, we considered two main approaches:

Approach (1)
Split the KVM_CREATE_GUEST_MEMFD ioctl into two separate ioctl: one to create the guest_memfd without a VM file (without struct kvm) descriptor, and another to attach a newly created VM file descriptor to a retrieved guest_memfd.

Introducing a new ioctl is in itself a problem (UAPI). Currently, a guest_memfd file belongs to a single VM. Decoupling creation and attachment could allow a guest_memfd to be attached to any VM, or shared among multiple VMs when passed at different offsets. Fully supporting this feature would require extensive work, and it is unclear if there are any non-LUO use cases that justify this complexity. There is related work going on here [4], but not exactly same. It still does not allow guest_memfd to be created without vm_fd. But there be other ways to use it, I would like to discuss the idea.

Approach (2)
Leverage a companion patch [3] (Also added as part of this series PATCH[1]) that allows one file to retrieve another file from the same LUO session. This enables the guest_memfd retrieval path to obtain the preserved KVM file, use it during guest_memfd file creation, and subsequently populate its preserved memory.

Preserving the KVM file allows us to preserve additional VM-specific metadata, which will be crucial in the future for cleanly resuming the VM. Currently, it preserves only the VM type and kvm->mem_attr_array.

Though the ongoing in-place sharing series [1] transfers attributes to the guest_memfd file, But preserving the kvm file opens the opportunity to preserve other VM state in future like registers state, vCPU etc.

Having the extensive usecases for preserving the kvm file, I went ahead with Approach (2). In future, if approach (1) become possible, it can easily be integrated with approach (2).

Following the first approach (preserving vm_fd along with guest_memfd),

** VM FILE LIVEUPDATE ** PATCH[3] && [4]

*PATCH[3]* has refactored few functions to support kvm preservation. During retrieval, vm_file needs to be recreated which will require kvm api. This patch exports those APIs. There is a new addition to struct kvm, vm_file. Which will be used by guest_memfd. I will discuss about this later.

*PATCH[4]*
The preservation of the vm file is straightforward.

On the retrieval path:
KVM normally requires a unique identifier (fdname) upon creation, which KVM typically assigns based on the newly created file descriptor number. However, in the LUO retrieval path, the retrieve call restores the underlying file structure and delegates actual file descriptor allocation to LUO (check luo_session_retrieve_fd). Currently, I used an atomically incremented sequence number as the fdname. I would like to discuss whether userspace services rely on specific naming conventions here. Or if we can change underlying the retrieve call (luo_retrieve_file) to pass fd?

**GUEST_MEMFD FILE LIVEUPDATE** PATCH[5], [6] & [7]

*PATCH[5]*
During retrieval of guest_memfd file, for its creation, this patch has exported APIs from guest_memfd.c to be used for guest_memfd_luo.c

*PATCH[6]*
This patch implements the API for gmem inode freeze, which freeze the fallocate operation on this inode. Freeze check can be extended in future to prevent new page faults as well, when liveupdate support for non-pre-faulted guest_memfd will be implemented.

*PATCH[7]*
Preservation Path:
We have discussed about this before,
I would like to add to that and discuss here a major design decision:
"Preservation order in between VM File and guest_memfd file"

Preservation Ordering is required because guest_memfd needs to store vm file token as one of its data, which it can use during retrieval to get the vm file and use (file->private_data: struct kvm ) for its creation using [3]. So KVM file must be preserved before guest_memfd file, so that guest_memfd preserve call can find vm file token from the same luo session.

Currently My preservation implementation does not require any strict ordering, they can be preserved in any sequence from userspace. I achieved this by implementing the freeze call for guest_memfd which gets run at the end just before kexec. This call freeze the luo session and no further changes can be done to the session. Inside guest_memfd luo_freeze handler, I update the token for vm_file. Which enable us to preserve the vm file and guest_memfd file in any order.

The drawback is, incase vm_file is not preserved, freeze will fail. And in enforcing the preserving order fails the guest_memfd preservation from the start. As with VM preservation will evolve in future, it will keep getting complicated so avoiding the preservation order should be the better choice to make the userspace simpler. I would be happy to disucss on this further.

To get the token, we need the vm_file and there is no way to get the vm_file from the struct kvm, as guest_memfd file only store the struct kvm. I have introduced a new member in struct kvm, vm_file. But with weak circular dependency as it is just to get the pointer for the file. we don't want to keep the reference of the file as vm_file takes for the kvm to keep itself (vm_file) alive. So whenever there is a need to use of kvm->vm_file, we take the reference and drop it suddenly.

Retrieval Path:
During retrieval path, we just retrieve the data from kho and populate into the newly created guest_memfd.
To create guest_memfd itself, it needs struct kvm, as we discussed above, which will come from vm_file, hence retrieval order is needed here. VM file needs to be retrieved first before guest_memfd.

To handle this situation, I had three approaches in mind with their own pros and cons:

Approach (1):
Use [3], retrieve internally using liveupdate_get_file_incoming which inherently retrieves the file incase it was not retrieved by the userspace already. But this creates an scenerio, that userspace might call luo_finish which will drop all the references of vm_file (and userspace not holding any as it has not retrieved it yet explicitly). And vm_file will get released. But this is a valid situation as when vm is going to be put down. Userspace can close the vm_fd and have guest_memfd yet opened and so other user of struct kvm like vCPUs etc. Only thing, this makes retrieved guest_memfd unusable unless, there is a mechanism to link to another VM (Nope).
This leaves us with following situation:
(A): As it is a valid situation, We can leave it as it, No retrieval order enforcement.
(B): We can implement can_finish to check if userspace has retrieved the vm_file, otherwise can stop luo_finish from succeeding, but I did not find a way to implement such check.
Approach (2):
Enforce the strict order, by implementing a new call which will first check whether the vm_file is retrieved or not, if not, it will not retrieve it internally and retrurn err to the caller which is guest_memfd retrieve function in this case. So guest_memfd can report the userspace about this error.

I have implemented Approach (1)(A), as it is a valid case, and does not enforce any retrieve order on userspace, which relieves the burden from the userspace when vm_file preservation will evolve. But userspace is now expected to retrieve the vm_file before calling luo_finish or guest_memfd will become unusable. As per LUO philosphy, It is userspace error.

**KERNEL SELFTEST FOR POC** PATCH[8] & [9]

*PATCH[8]* refactor kvm selftest framework to expose some raw apis to setup the VM.
*PATCH[9]* implements the basic test, where it spawn a VM with guest_memfd or 16MB and fault it completely and write data to its 5MB portion. After LUO preserve call, and kexec, On retrieve, a new VM is spawn with the restored vm_file and restored guest_memfd and the data is verified.

I will update this test in the next version to use the liveupdate selftests library [5].

Future Work:
1. To support preservation for non-prefaulted guest_memfd to save memory in KHO. (Already working on this, will post another series soon)
2. Support private guest_memfd preservation.
3. Extend the support for guest_memfd with in-place conversion of shared/private.

[1] https://lore.kernel.org/all/[email protected]/
[2] https://lore.kernel.org/all/[email protected]/
[3] https://lore.kernel.org/all/[email protected]/
[4] https://lore.kernel.org/all/[email protected]/
[5] https://lore.kernel.org/all/[email protected]/

Pasha Tatashin (1):
liveupdate: luo_file: Add internal APIs for file preservation

Tarun Sahu (8):
liveupdate: Add LIVEUPDATE_GUEST_MEMFD config option
kvm: Prepare core VM structs and helpers for LUO support
kvm: kvm_luo: Allow kvm preservation with LUO
kvm: guest_memfd: Move internal definitions and helper to new header
kvm: guest_memfd: Add support for freezing and unfreezing mappings
kvm: guest_memfd_luo: add support for guest_memfd preservation
selftests: kvm: Split ____vm_create() to expose init helpers
selftests: kvm: Add guest_memfd_preservation_test

 MAINTAINERS                                   |  13 +
 include/linux/kho/abi/kvm.h                   | 121 +++++
 include/linux/kvm_host.h                      |  14 +
 include/linux/liveupdate.h                    |  21 +
 kernel/liveupdate/Kconfig                     |  15 +
 kernel/liveupdate/luo_file.c                  |  69 +++
 kernel/liveupdate/luo_internal.h              |  17 +
 tools/testing/selftests/kvm/Makefile.kvm      |   2 +
 .../kvm/guest_memfd_preservation_test.c       | 285 ++++++++++
 .../testing/selftests/kvm/include/kvm_util.h  |   2 +
 tools/testing/selftests/kvm/lib/kvm_util.c    |  26 +-
 virt/kvm/Makefile.kvm                         |   1 +
 virt/kvm/guest_memfd.c                        | 180 +++++--
 virt/kvm/guest_memfd.h                        |  44 ++
 virt/kvm/guest_memfd_luo.c                    | 495 ++++++++++++++++++
 virt/kvm/kvm_luo.c                            | 346 ++++++++++++
 virt/kvm/kvm_main.c                           |  79 ++-
 virt/kvm/kvm_mm.h                             |   3 +
 18 files changed, 1653 insertions(+), 80 deletions(-)
 create mode 100644 include/linux/kho/abi/kvm.h
 create mode 100644 tools/testing/selftests/kvm/guest_memfd_preservation_test.c
 create mode 100644 virt/kvm/guest_memfd.h
 create mode 100644 virt/kvm/guest_memfd_luo.c
 create mode 100644 virt/kvm/kvm_luo.c

base-commit: 5200f5f493f79f14bbdc349e402a40dfb32f23c8

Signature · 1 line
2.54.0.563.g4f69b47b94-goog
author
#1 lore raw

RFC PATCH 1/9 liveupdate: luo_file: Add internal APIs for file preservation

From: Pasha Tatashin <[email protected]>

The core liveupdate mechanism allows userspace to preserve file descriptors. However, kernel subsystems often manage struct file objects directly and need to participate in the preservation process programmatically without relying solely on userspace interaction.

---
 include/linux/liveupdate.h       | 21 ++++++++++
 kernel/liveupdate/luo_file.c     | 69 ++++++++++++++++++++++++++++++++
 kernel/liveupdate/luo_internal.h | 17 ++++++++
 3 files changed, 107 insertions(+)
include/linux/liveupdate.h +21 -0
index 30c5a39ff9e9..de052438eaac 100644
@@ -24,6 +24,7 @@ struct file;
 /**
  * struct liveupdate_file_op_args - Arguments for file operation callbacks.
  * @handler:          The file handler being called.
+ * @session:          The session this file belongs to.
  * @retrieve_status:  The retrieve status for the 'can_finish / finish'
  *                    operation. A value of 0 means the retrieve has not been
  *                    attempted, a positive value means the retrieve was
@@ -44,6 +45,7 @@ struct file;
  */
 struct liveupdate_file_op_args {
 	struct liveupdate_file_handler *handler;
+	struct liveupdate_session *session;
 	int retrieve_status;
 	struct file *file;
 	u64 serialized_data;
@@ -240,6 +242,13 @@ void liveupdate_unregister_flb(struct liveupdate_file_handler *fh,
 
 int liveupdate_flb_get_incoming(struct liveupdate_flb *flb, void **objp);
 int liveupdate_flb_get_outgoing(struct liveupdate_flb *flb, void **objp);
+/* kernel can internally retrieve files */
+int liveupdate_get_file_incoming(struct liveupdate_session *s, u64 token,
+				 struct file **filep);
+
+/* Get a token for an outgoing file, or -ENOENT if file is not preserved */
+int liveupdate_get_token_outgoing(struct liveupdate_session *s,
+				  struct file *file, u64 *tokenp);
 
 #else /* CONFIG_LIVEUPDATE */
 
@@ -285,5 +294,17 @@ static inline int liveupdate_flb_get_outgoing(struct liveupdate_flb *flb,
 	return -EOPNOTSUPP;
 }
 
+static inline int liveupdate_get_file_incoming(struct liveupdate_session *s,
+					       u64 token, struct file **filep)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline int liveupdate_get_token_outgoing(struct liveupdate_session *s,
+						struct file *file, u64 *tokenp)
+{
+	return -EOPNOTSUPP;
+}
+
 #endif /* CONFIG_LIVEUPDATE */
 #endif /* _LINUX_LIVEUPDATE_H */
kernel/liveupdate/luo_file.c +69 -0

114 lines not on this page · raw message

kernel/liveupdate/luo_internal.h +17 -0
index 875844d7a41d..08b198802e7f 100644
@@ -79,6 +79,23 @@ struct luo_session {
 
 extern struct rw_semaphore luo_register_rwlock;
 
+static inline struct liveupdate_session *luo_session_from_file_set(struct luo_file_set *file_set)
+{
+	struct luo_session *session;
+
+	session = container_of(file_set, struct luo_session, file_set);
+
+	return (struct liveupdate_session *)session;
+}
+
+static inline struct luo_file_set *luo_file_set_from_session_locked(struct liveupdate_session *s)
+{
+	struct luo_session *session = (struct luo_session *)s;
+
+	lockdep_assert_held(&session->mutex);
+	return &session->file_set;
+}
+
 int luo_session_create(const char *name, struct file **filep);
 int luo_session_retrieve(const char *name, struct file **filep);
 int __init luo_session_setup_outgoing(void *fdt);
Signature · 1 line
2.54.0.563.g4f69b47b94-goog
author
#2 lore raw

RFC PATCH 2/9 liveupdate: Add LIVEUPDATE_GUEST_MEMFD config option

In reply to Tarun Sahu

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 and pre-faulted.

---
 kernel/liveupdate/Kconfig | 15 +++++++++++++++
 1 file changed, 15 insertions(+)
kernel/liveupdate/Kconfig +15 -0
index 1a8513f16ef7..0bbc4037192e 100644
@@ -88,4 +88,19 @@ config LIVEUPDATE_MEMFD
 
 	  If unsure, say N.
 
+config LIVEUPDATE_GUEST_MEMFD
+	bool "Live update support for guest_memfd"
+	depends on LIVEUPDATE
+	depends on KVM_GUEST_MEMFD
+	default LIVEUPDATE
+	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
Signature · 1 line
2.54.0.563.g4f69b47b94-goog
author
#3 lore raw

RFC PATCH 3/9 kvm: Prepare core VM structs and helpers for LUO support

In reply to Tarun Sahu

Introduce core infrastructure to support VM preservation with LUO.

First two changes are just refactoring, no functional change, third change introduces a new member in struct kvm.
- Move ITOA_MAX_LEN to kvm_mm.h for reuse by upcoming kvm_luo code.
- Add a public kvm_create_vm_file() helper wrapping kvm_create_vm() and anon_inode_getfile() to provide a unified VM file creation API.
- Track a weak reference to the backing file in struct kvm under CONFIG_LIVEUPDATE_GUEST_MEMFD to enable reverse file resolution without circular lifetime dependencies.

---
 include/linux/kvm_host.h | 14 +++++++
 virt/kvm/kvm_main.c      | 79 +++++++++++++++++++++++++++++-----------
 virt/kvm/kvm_mm.h        |  3 ++
 3 files changed, 75 insertions(+), 21 deletions(-)
include/linux/kvm_host.h +14 -0
index 4c14aee1fb06..9111a28637af 100644
@@ -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 *vm_file;
 #endif
 	char stats_id[KVM_STATS_NAME_SIZE];
 };
@@ -1074,7 +1086,9 @@ 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);
+void kvm_uevent_notify_vm_create(struct kvm *kvm);
 
 static inline struct kvm_memslots *__kvm_memslots(struct kvm *kvm, int as_id)
 {
virt/kvm/kvm_main.c +58 -21

121 lines not on this page · raw message

virt/kvm/kvm_mm.h +3 -0
index 9fcc5d5b7f8d..7aa1d65c3d46 100644
@@ -3,6 +3,9 @@
 #ifndef __KVM_MM_H__
 #define __KVM_MM_H__ 1
 
+/* 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
Signature · 1 line
2.54.0.563.g4f69b47b94-goog
author
#4 lore raw

RFC PATCH 4/9 kvm: kvm_luo: Allow kvm preservation with LUO

In reply to Tarun Sahu

Introduce KVM VM preservation support for Live Update Orchestrator.

Register an LUO file handler for KVM files to serialize and deserialize necessary VM state across live updates. Currently, this preserves the VM type and generic memory attributes. This implementation provides the necessary infrastructure and dependencies for the upcoming guest_memfd preservation support. And it can be extended to preserve more vm state in future.

To preserve the kvm file it is necessary that the attributes that we are preserving must not change while or after preservation. The memory attribute change request is triggered by Guest to KVM and exit to VMM. VMM is aware that liveupdate is in progress and is expected to cancel this request Or pause the VM. This ensures that no change in memory attributes from guest are introduced while/after preservation of kvm.

Retrieve is simply creating the kvm and populate the retrieved data. Only catch here is there is no way to know which fd is going to be assigned to this kvm file hence I am using atomically incremented id for the fdname.

This change also updates the MAINTAINERS list for kvm_luo.c.

---
My only worry is if userspace strictly depends on the fdname, that it needs to be consistent with vm_fd. Discussed more details in the cover letter. Would really appreciates the alternatives/other approaches.

---
 MAINTAINERS                 |  11 ++
 include/linux/kho/abi/kvm.h |  54 ++++++
 virt/kvm/Makefile.kvm       |   1 +
 virt/kvm/kvm_luo.c          | 346 ++++++++++++++++++++++++++++++++++++
 4 files changed, 412 insertions(+)
 create mode 100644 include/linux/kho/abi/kvm.h
 create mode 100644 virt/kvm/kvm_luo.c
MAINTAINERS +11 -0
index c2c6d79275c6..2c26eb17bc0a 100644
@@ -14404,6 +14404,17 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/leds/backlight/kinetic,ktz8866.yaml
 F:	drivers/video/backlight/ktz8866.c
 
+KVM LIVE UPDATE
+M:	Pasha Tatashin <[email protected]>
+M:	Mike Rapoport <[email protected]>
+M:	Pratyush Yadav <[email protected]>
+R:	Tarun Sahu <[email protected]>
+L:	[email protected]
+L:	[email protected]
+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 <[email protected]>
 R:	Vitaly Kuznetsov <[email protected]>
include/linux/kho/abi/kvm.h new file +54 -0

54 lines not on this page · raw message

virt/kvm/Makefile.kvm +1 -0
index d047d4cf58c9..c1a962159264 100644
@@ -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
virt/kvm/kvm_luo.c new file +346 -0

346 lines not on this page · raw message

Signature · 1 line
2.54.0.563.g4f69b47b94-goog
author
#5 lore raw

RFC PATCH 5/9 kvm: guest_memfd: Move internal definitions and helper to new header

In reply to Tarun Sahu

To support guest_memfd memory preservation with LUO, guest_memfd luo code needs to access guest_memfd internals and reconstruct guest_memfd file instances from a preserved state.

Extract gmem_file, gmem_inode, and the GMEM_I() helper from guest_memfd.c into a new internal header virt/kvm/guest_memfd.h.

Additionally, split __kvm_gmem_create() to expose a non-static __kvm_gmem_create_file() helper. This helper returns a struct file instead of a file descriptor, enabling file creation and initialization without installing it into a file descriptor table.

---
 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
virt/kvm/guest_memfd.c +28 -40
index 69c9d6d546b2..6740ae2bf948 100644
@@ -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)
@@ -556,23 +530,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)) {
@@ -611,8 +579,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);
@@ -620,7 +587,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;
 }
virt/kvm/guest_memfd.h new file +39 -0
new file mode 100644
index 000000000000..c528b046dd69
@@ -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__ */
Signature · 1 line
2.54.0.563.g4f69b47b94-goog
author
#6 lore raw

RFC PATCH 6/9 kvm: guest_memfd: Add support for freezing and unfreezing mappings

In reply to Tarun Sahu

This patch introduces the freeze on gmem_inode which prevents the fallocate call. This will avoid gmem file modification when it is being preserved

Used srcu lock to synchronise the freeze call, where write blocks until all the reads are free. And reads are re-entrant.

This can be extended to freeze the fault path as well. But currently the fault failure due to sudden freeze might be fatal to the running guest.

---
 virt/kvm/guest_memfd.c | 112 +++++++++++++++++++++++++++++++++++++----
 virt/kvm/guest_memfd.h |   5 ++
 2 files changed, 106 insertions(+), 11 deletions(-)
virt/kvm/guest_memfd.c +101 -11
index 6740ae2bf948..91e42f717286 100644
@@ -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
@@ -273,16 +276,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 +308,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;
 }
 
@@ -944,7 +964,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 = {
@@ -1001,12 +1023,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)
@@ -1014,5 +1045,64 @@ 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.
+ *
+ * NOTE: Currently It is only checked at fallocate path. Page fault path NOT
+ * touched.
+ *
+ * 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`):
+ *
+ * Though currently, the freeze is checked only in fallocate, but it might be needed
+ * to the fault path as well in future to completely freeze the inode.
+ *
+ * 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;
+}
virt/kvm/guest_memfd.h +5 -0
index c528b046dd69..028c348a1023 100644
@@ -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__ */
Signature · 1 line
2.54.0.563.g4f69b47b94-goog
author
#7 lore raw

RFC PATCH 7/9 kvm: guest_memfd_luo: add support for guest_memfd preservation

In reply to Tarun Sahu

This patch sets up the basic infrastructure to preserve the guest_memfd. Currently this supports only fully shared guest_memfd (INIT_SHARED), pre-faulted and backed by PAGE_SIZE pages.

It registers a new LUO file handler for guest_memfd files to serialize and deserialize guest memory. This allows preserving guest memory backed by guest_memfd across updates, ensuring that guest instances can be resumed seamlessly without losing their memory contents.

Preservation is straight forward. It walks through the folios and serialize them.

There is kvm_gmem_freeze call on preserve which freeze the guest_memfd inode. It avoids any changes to inode mapping with fallocate calls on or after preservation. No need to check this during the page fault as preservation is only supported for pre-faulted/pre-allocated guest_memfd.

While retrieving the guest_memfd, it requires the struct kvm to create new guest_memfd. So it first get the vm_file from the same session using the token passed during the preservation. And use it to get vm_file->kvm.

This change also update the MAINTAINERS list.

---
Also, I wanted to use the luo file handler compatible string for guest_memfd_luo same as kvm_luo (KVM_LUO_FH_COMPATIBLE), but unfortnately LUO design does not permit this, every handler needs to be registered with the separate string.

---
 MAINTAINERS                 |   1 +
 include/linux/kho/abi/kvm.h |  79 +++++-
 virt/kvm/Makefile.kvm       |   2 +-
 virt/kvm/guest_memfd_luo.c  | 495 ++++++++++++++++++++++++++++++++++++
 4 files changed, 570 insertions(+), 7 deletions(-)
 create mode 100644 virt/kvm/guest_memfd_luo.c
MAINTAINERS +1 -0
index 2c26eb17bc0a..e5402a56ab98 100644
@@ -14413,6 +14413,7 @@ L:	[email protected]
 L:	[email protected]
 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)
include/linux/kho/abi/kvm.h +73 -6

97 lines not on this page · raw message

virt/kvm/Makefile.kvm +1 -1
index c1a962159264..d30fca094c42 100644
@@ -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
virt/kvm/guest_memfd_luo.c new file +495 -0

495 lines not on this page · raw message

Signature · 1 line
2.54.0.563.g4f69b47b94-goog
author
#8 lore raw

RFC PATCH 8/9 selftests: kvm: Split ____vm_create() to expose init helpers

In reply to Tarun Sahu

Refactor ____vm_create() in the KVM selftest library to extract its initialization steps into separate, reusable internal helpers.

Introduce vm_init_fields() and vm_init_memory_properties(). This allows advanced test setups to perform targeted VM fields or memory property initializations independently, which is required by upcoming test cases that restore preserved VMs. No functional changes are introduced for the existing tests.

---
 .../testing/selftests/kvm/include/kvm_util.h  |  2 ++
 tools/testing/selftests/kvm/lib/kvm_util.c    | 26 +++++++++++++------
 2 files changed, 20 insertions(+), 8 deletions(-)
tools/testing/selftests/kvm/include/kvm_util.h +2 -0
index 2ecaaa0e9965..d10cd25d0658 100644
@@ -471,6 +471,8 @@ 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);
 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);
tools/testing/selftests/kvm/lib/kvm_util.c +18 -8
index 2a76eca7029d..f4cd06d34ce9 100644
@@ -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,6 +388,20 @@ 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;
 }
Signature · 1 line
2.54.0.563.g4f69b47b94-goog
author
#9 lore raw

RFC PATCH 9/9 selftests: kvm: Add guest_memfd_preservation_test

In reply to Tarun Sahu

Add a new KVM selftest guest_memfd_preservation_test to verify that guest memory backed by guest_memfd is preserved properly.

The test leverages the Live Update Orchestrator (LUO) infrastructure to validate that memory folios and configuration layouts are successfully saved and then restored during kernel live updates, preventing any memory loss for the guest.

Here, I have used the kvm selftests framework by creating a new vm and mapping two memory slots to it. One is the code that is executed inside the vm and other is the guest_memfd whose memory is being written by the guest code.

In Phase 1: Once data is written the vm exits and wait for the user to trigger the kexec.

In Phase 2: A new vm is created with retrieved kvm and again two memory slots are assigned. Once for guest code, and another is for retrieved guest_memfd where guest_memfd memory is verified by the executed guest code. If verification succeeds, The test passes.

---
 MAINTAINERS                                   |   1 +
 tools/testing/selftests/kvm/Makefile.kvm      |   2 +
 .../kvm/guest_memfd_preservation_test.c       | 285 ++++++++++++++++++
 3 files changed, 288 insertions(+)
 create mode 100644 tools/testing/selftests/kvm/guest_memfd_preservation_test.c
MAINTAINERS +1 -0
index e5402a56ab98..647d60f6a1e2 100644
@@ -14413,6 +14413,7 @@ L:	[email protected]
 L:	[email protected]
 S:	Maintained
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux.git
+F:	tools/testing/selftests/kvm/guest_memfd_preservation_test.c
 F:	virt/kvm/guest_memfd_luo.c
 F:	virt/kvm/kvm_luo.c
 
tools/testing/selftests/kvm/Makefile.kvm +2 -0
index 9118a5a51b89..4ea6cb7bf001 100644
@@ -161,6 +161,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
tools/testing/selftests/kvm/guest_memfd_preservation_test.c new file +285 -0

285 lines not on this page · raw message

Signature · 1 line
2.54.0.563.g4f69b47b94-goog
#10 lore raw
In reply to Tarun Sahu

Tarun Sahu <[email protected]> writes:

Hi,

Based on the discussion on Bi-weekly guest_memfd upstream call.

The following suggestions are made:
1. Remove the prefaulted condition and During preservation, let the VMM have the policy to either pause the VM or avoid any upcoming faults/conversion (conversion is not part of this series, but design concept for future extension) occurs. If new fault/conversion requests are originated, Kernel will return error to notify VMM.

In future, We will revisit if this functionality can be extended to handle post-preserve fault/conversion with having data on preservation latency and complexity. Pratyush is already working on implementing similar thing for memfd and hugetlb preservation.

2. We are going with preserving the vm file along with guest_memfd file

3. No need to preserve kvm->mem_attr_array, as this is on deprecating path and For this series, where only fully shared guest_memfd is supported for liveupdate, preservation of these attributes not required.

Due to time constraints:
we have one open Question left:
1. How to check guest_memfd is fully shared. Currently that is being done by INIT_SHARED flag. But with in-place it is going to change its meaning and it will mean only durng the creation of guest_memfd it is shared whose folios can be converted to private.
So for this series, I am looking for suggestion, what is the best way to find the sharedness of guest_memfd which will be compatible with in-place series. OR we can go like this (my personnel recommendation):

if (guest_memfd_is_fully_shared())
return true; // support the preservation

   // This series
   int guest_memfd_is_fully_shared(void) {
       if (guest_memfd->flags & GUEST_MEMFD_FLAG_INIT_SHARED)
          return true;
         return false;
   }

TO::
// Once in-place conversion series lands.

   int guest_memfd_is_fully_shared(void) {
       if (No Private flags in (guest_memfd->attributes))
          return true;
         return false;

}

~ Tarun

Quoted 320 lines

Hello,

I am proposing this series as RFC, to initiate the discussion for supporting the guest_memfd preservation. This will setup basic arhitecture for VM preservation during liveupdate. This Cover letter has three sections (please feel free to skip the on you already know):

A. Guest_memfd introduction:
To make the audience familiar with guest_memfd
B. Liveupdate introduction:
To make the audience familiar with liveupdate
C. Actual Implementation Design and questions.

**GUEST MEMFD INTRODUCTION**

Initially, guest_memfd was created to support guest private memory in confidential computing VMs (CoCo VMs). It was designed so that whenever a guest wants to grant the host access to private memory, a series of calls occurs: from the guest to KVM, KVM to the host userspace, host userspace back to KVM, and finally a new page fault maps the memory into a separate shared address space. Conversely, if the guest transitions the memory back to private, the subsequent fault is handled by guest_memfd. (Dual Mapping Architecture). In such a VM, all guest memory is initially shared. On the fly, the guest may request to change pages to private; the metadata indicating which parts of memory are private is stored in an xarray inside struct kvm (mem_attr_array). This array serves as the source of truth for the fault mechanism, determining whether a mapping should be created from host-userspace-mapped pages or directly from the guest_memfd file. For private memory, Fault also calls architecture-specific function to set up private hardware access (e.g., on SEV-SNP or TDX). This type of guest_memfd is fully-private where shared mapping comes from userspace mapped address space.

Subsequently, support was added to allow the entire guest memory to be backed by guest_memfd. This led to the implementation of the MMAP and INIT_SHARED flags for the guest_memfd inode. When KVM_CREATE_GUEST_MEMFD is called with these flags, the guest_memfd becomes mmap-able by host userspace. The INIT_SHARED flag is used to make the guest_memfd completely shared between the host and the guest. Consequently, page faults from both host userspace and the guest resolve to the same guest_memfd page cache. However, under this configuration, marking a portion of this memory as private is not possible. This type of guest_memfd is fully-shared.

If guest_memfd is created with INIT_SHARED without MMAP, the host can never access the guest_memfd. But the memory is still considered shared.

Hence, At this point, Only use-case of guest_memfd is either fully-shared or fully-private.

There is ongoing work to make shared and private mapping in-place backed by guest_memfd. [1] There is also ongoing work to back guest_memfd by hugetlb pages. [2]

**LIVEUPDATE INTRODUCTION (LIVEUPDATE ORCHESTRATOR - LUO)**

Livepdate support was added in kernel to update the host kernel by minimizing the downtime to minimal. This is generally achieved by preserving the current state of the system and retrieve after boot to resume from where we left it.

Any subsystem that wants to preserve themselves, register their handler with liveupdate system. This handler includes calls to the following

*can_preserve (file)*:
This tells the luo system about the eligibility of the file. When preserve ioctl is called, it first loop through all the file handlers and call can_preserve, the one which return true, luo uses this file handler fh->preserve call to preserve the file.

*preserve(file)*:
This actually preserves the file.

*unpreserve(file)*:
This unpreserve the file incase userspace want to go back.

*retrieve(file)*:
On new kernel boot, this function retrieves the file.

*finish(file)*:
When userspace decides that all the files in the liveupdate session has been retrieved, it can trigger this to do final work of cleaning up.

LUO preserve its memory using KHO (kexec-handover). All these APIs will be implemented using KHO calls.

**GUEST MEMFD PRESERVATION**

This patch sets up the basic infrastructure to preserve the guest_memfd. Currently this supports only fully-shared, pre-faulted guest_memfd (INIT_SHARED) backed by PAGE_SIZE pages.

It registers a new LUO file handler for guest_memfd file to serialize and deserialize guest memory. This allows preserving guest memory backed by guest_memfd across updates, ensuring that guest instances can be resumed seamlessly without losing their memory contents.

The preservation call is straight forward. It walks through the page cache, serialize the folios and preserve them.

On the retrieval path:
Currently, creating a guest_memfd requires an associated struct kvm (derived from vm_file / vm_fd). Since there is no direct way to pass a VM file descriptor via the LUO API, we considered two main approaches:

Approach (1)
Split the KVM_CREATE_GUEST_MEMFD ioctl into two separate ioctl: one to create the guest_memfd without a VM file (without struct kvm) descriptor, and another to attach a newly created VM file descriptor to a retrieved guest_memfd.

Introducing a new ioctl is in itself a problem (UAPI). Currently, a guest_memfd file belongs to a single VM. Decoupling creation and attachment could allow a guest_memfd to be attached to any VM, or shared among multiple VMs when passed at different offsets. Fully supporting this feature would require extensive work, and it is unclear if there are any non-LUO use cases that justify this complexity. There is related work going on here [4], but not exactly same. It still does not allow guest_memfd to be created without vm_fd. But there be other ways to use it, I would like to discuss the idea.

Approach (2)
Leverage a companion patch [3] (Also added as part of this series PATCH[1]) that allows one file to retrieve another file from the same LUO session. This enables the guest_memfd retrieval path to obtain the preserved KVM file, use it during guest_memfd file creation, and subsequently populate its preserved memory.

Preserving the KVM file allows us to preserve additional VM-specific metadata, which will be crucial in the future for cleanly resuming the VM. Currently, it preserves only the VM type and kvm->mem_attr_array.

Though the ongoing in-place sharing series [1] transfers attributes to the guest_memfd file, But preserving the kvm file opens the opportunity to preserve other VM state in future like registers state, vCPU etc.

Having the extensive usecases for preserving the kvm file, I went ahead with Approach (2). In future, if approach (1) become possible, it can easily be integrated with approach (2).

Following the first approach (preserving vm_fd along with guest_memfd),

** VM FILE LIVEUPDATE ** PATCH[3] && [4]

*PATCH[3]* has refactored few functions to support kvm preservation. During retrieval, vm_file needs to be recreated which will require kvm api. This patch exports those APIs. There is a new addition to struct kvm, vm_file. Which will be used by guest_memfd. I will discuss about this later.

*PATCH[4]*
The preservation of the vm file is straightforward.

On the retrieval path:
KVM normally requires a unique identifier (fdname) upon creation, which KVM typically assigns based on the newly created file descriptor number. However, in the LUO retrieval path, the retrieve call restores the underlying file structure and delegates actual file descriptor allocation to LUO (check luo_session_retrieve_fd). Currently, I used an atomically incremented sequence number as the fdname. I would like to discuss whether userspace services rely on specific naming conventions here. Or if we can change underlying the retrieve call (luo_retrieve_file) to pass fd?

**GUEST_MEMFD FILE LIVEUPDATE** PATCH[5], [6] & [7]

*PATCH[5]*
During retrieval of guest_memfd file, for its creation, this patch has exported APIs from guest_memfd.c to be used for guest_memfd_luo.c

*PATCH[6]*
This patch implements the API for gmem inode freeze, which freeze the fallocate operation on this inode. Freeze check can be extended in future to prevent new page faults as well, when liveupdate support for non-pre-faulted guest_memfd will be implemented.

*PATCH[7]*
Preservation Path:
We have discussed about this before,
I would like to add to that and discuss here a major design decision:
"Preservation order in between VM File and guest_memfd file"

Preservation Ordering is required because guest_memfd needs to store vm file token as one of its data, which it can use during retrieval to get the vm file and use (file->private_data: struct kvm ) for its creation using [3]. So KVM file must be preserved before guest_memfd file, so that guest_memfd preserve call can find vm file token from the same luo session.

Currently My preservation implementation does not require any strict ordering, they can be preserved in any sequence from userspace. I achieved this by implementing the freeze call for guest_memfd which gets run at the end just before kexec. This call freeze the luo session and no further changes can be done to the session. Inside guest_memfd luo_freeze handler, I update the token for vm_file. Which enable us to preserve the vm file and guest_memfd file in any order.

The drawback is, incase vm_file is not preserved, freeze will fail. And in enforcing the preserving order fails the guest_memfd preservation from the start. As with VM preservation will evolve in future, it will keep getting complicated so avoiding the preservation order should be the better choice to make the userspace simpler. I would be happy to disucss on this further.

To get the token, we need the vm_file and there is no way to get the vm_file from the struct kvm, as guest_memfd file only store the struct kvm. I have introduced a new member in struct kvm, vm_file. But with weak circular dependency as it is just to get the pointer for the file. we don't want to keep the reference of the file as vm_file takes for the kvm to keep itself (vm_file) alive. So whenever there is a need to use of kvm->vm_file, we take the reference and drop it suddenly.

Retrieval Path:
During retrieval path, we just retrieve the data from kho and populate into the newly created guest_memfd.
To create guest_memfd itself, it needs struct kvm, as we discussed above, which will come from vm_file, hence retrieval order is needed here. VM file needs to be retrieved first before guest_memfd.

To handle this situation, I had three approaches in mind with their own pros and cons:

Approach (1):
Use [3], retrieve internally using liveupdate_get_file_incoming which inherently retrieves the file incase it was not retrieved by the userspace already. But this creates an scenerio, that userspace might call luo_finish which will drop all the references of vm_file (and userspace not holding any as it has not retrieved it yet explicitly). And vm_file will get released. But this is a valid situation as when vm is going to be put down. Userspace can close the vm_fd and have guest_memfd yet opened and so other user of struct kvm like vCPUs etc. Only thing, this makes retrieved guest_memfd unusable unless, there is a mechanism to link to another VM (Nope).
This leaves us with following situation:
(A): As it is a valid situation, We can leave it as it, No retrieval order enforcement.
(B): We can implement can_finish to check if userspace has retrieved the vm_file, otherwise can stop luo_finish from succeeding, but I did not find a way to implement such check.
Approach (2):
Enforce the strict order, by implementing a new call which will first check whether the vm_file is retrieved or not, if not, it will not retrieve it internally and retrurn err to the caller which is guest_memfd retrieve function in this case. So guest_memfd can report the userspace about this error.

I have implemented Approach (1)(A), as it is a valid case, and does not enforce any retrieve order on userspace, which relieves the burden from the userspace when vm_file preservation will evolve. But userspace is now expected to retrieve the vm_file before calling luo_finish or guest_memfd will become unusable. As per LUO philosphy, It is userspace error.

**KERNEL SELFTEST FOR POC** PATCH[8] & [9]

*PATCH[8]* refactor kvm selftest framework to expose some raw apis to setup the VM.
*PATCH[9]* implements the basic test, where it spawn a VM with guest_memfd or 16MB and fault it completely and write data to its 5MB portion. After LUO preserve call, and kexec, On retrieve, a new VM is spawn with the restored vm_file and restored guest_memfd and the data is verified.

I will update this test in the next version to use the liveupdate selftests library [5].

Future Work:
1. To support preservation for non-prefaulted guest_memfd to save memory in KHO. (Already working on this, will post another series soon)
2. Support private guest_memfd preservation.
3. Extend the support for guest_memfd with in-place conversion of shared/private.

[1] https://lore.kernel.org/all/[email protected]/
[2] https://lore.kernel.org/all/[email protected]/
[3] https://lore.kernel.org/all/[email protected]/
[4] https://lore.kernel.org/all/[email protected]/
[5] https://lore.kernel.org/all/[email protected]/

Pasha Tatashin (1):
liveupdate: luo_file: Add internal APIs for file preservation

Tarun Sahu (8):
liveupdate: Add LIVEUPDATE_GUEST_MEMFD config option
kvm: Prepare core VM structs and helpers for LUO support
kvm: kvm_luo: Allow kvm preservation with LUO
kvm: guest_memfd: Move internal definitions and helper to new header
kvm: guest_memfd: Add support for freezing and unfreezing mappings
kvm: guest_memfd_luo: add support for guest_memfd preservation
selftests: kvm: Split ____vm_create() to expose init helpers
selftests: kvm: Add guest_memfd_preservation_test

 MAINTAINERS                                   |  13 +
 include/linux/kho/abi/kvm.h                   | 121 +++++
 include/linux/kvm_host.h                      |  14 +
 include/linux/liveupdate.h                    |  21 +
 kernel/liveupdate/Kconfig                     |  15 +
 kernel/liveupdate/luo_file.c                  |  69 +++
 kernel/liveupdate/luo_internal.h              |  17 +
 tools/testing/selftests/kvm/Makefile.kvm      |   2 +
 .../kvm/guest_memfd_preservation_test.c       | 285 ++++++++++
 .../testing/selftests/kvm/include/kvm_util.h  |   2 +
 tools/testing/selftests/kvm/lib/kvm_util.c    |  26 +-
 virt/kvm/Makefile.kvm                         |   1 +
 virt/kvm/guest_memfd.c                        | 180 +++++--
 virt/kvm/guest_memfd.h                        |  44 ++
 virt/kvm/guest_memfd_luo.c                    | 495 ++++++++++++++++++
 virt/kvm/kvm_luo.c                            | 346 ++++++++++++
 virt/kvm/kvm_main.c                           |  79 ++-
 virt/kvm/kvm_mm.h                             |   3 +
 18 files changed, 1653 insertions(+), 80 deletions(-)
 create mode 100644 include/linux/kho/abi/kvm.h
 create mode 100644 tools/testing/selftests/kvm/guest_memfd_preservation_test.c
 create mode 100644 virt/kvm/guest_memfd.h
 create mode 100644 virt/kvm/guest_memfd_luo.c
 create mode 100644 virt/kvm/kvm_luo.c

base-commit: 5200f5f493f79f14bbdc349e402a40dfb32f23c8

Signature · 1 line
2.54.0.563.g4f69b47b94-goog
#11 lore raw
Quoted 25 lines · On Thu, May 28, 2026, [email protected] wrote:

3. No need to preserve kvm->mem_attr_array, as this is on deprecating path and For this series, where only fully shared guest_memfd is supported for liveupdate, preservation of these attributes not required.

Due to time constraints:
we have one open Question left:
1. How to check guest_memfd is fully shared. Currently that is being done by INIT_SHARED flag. But with in-place it is going to change its meaning and it will mean only durng the creation of guest_memfd it is shared whose folios can be converted to private.
So for this series, I am looking for suggestion, what is the best way to find the sharedness of guest_memfd which will be compatible with in-place series. OR we can go like this (my personnel recommendation):

   if (guest_memfd_is_fully_shared())
      return true; // support the preservation

   // This series
   int guest_memfd_is_fully_shared(void) {
       if (guest_memfd->flags & GUEST_MEMFD_FLAG_INIT_SHARED)
          return true;
         return false;
   }
Quoted 7 lines
   TO::
   // Once in-place conversion series lands.
   int guest_memfd_is_fully_shared(void) {
       if (No Private flags in (guest_memfd->attributes))
          return true;
         return false;
   }

Given that lack of support isn't going to be limited to _just_ guest_memfd, simply disallow preservation if the VM supports private memory:

	if (kvm_arch_has_private_mem(kvm))
		return -EOPNOTSUPP;
#12 lore raw
Quoted 6 lines · Sean Christopherson
Quoted 6 lines

[...snip...]

we have one open Question left:
1. How to check guest_memfd is fully shared.

Quoted 8 lines
Quoted 2 lines

[...snip...]

Given that lack of support isn't going to be limited to _just_ guest_memfd, simply disallow preservation if the VM supports private memory:

	if (kvm_arch_has_private_mem(kvm))
		return -EOPNOTSUPP;

Makes sense. Tarun this was the other option that I was suggesting when we discussed offline.

I think (?) it is possible to create a fully-private guest_memfd for a non-Confidential VM, and even after conversion lands, for both vm_memory_attributes=true and vm_memory_attributes=false.

In that case, your preservation series can still preserve memory tracked as private by guest_memfd but not used as private, right?

I don't think anyone will use this combination before guest_memfd write() support lands, we just need to make sure there's no kernel crash or corruption in this case.

#13 lore raw

Hi,

Quoted 8 lines · Ackerley Tng
Quoted 6 lines · Sean Christopherson
Quoted 6 lines

[...snip...]

we have one open Question left:
1. How to check guest_memfd is fully shared.

Quoted 9 lines
Quoted 8 lines
Quoted 2 lines

[...snip...]

Given that lack of support isn't going to be limited to _just_ guest_memfd, simply disallow preservation if the VM supports private memory:

	if (kvm_arch_has_private_mem(kvm))
		return -EOPNOTSUPP;

This is a good idea, I have implemented it in V2 (sent it recently).

Below, I have mentioned a detailed analysis. Please let me know your thoughts.

Quoted 7 lines

Makes sense. Tarun this was the other option that I was suggesting when we discussed offline.

I think (?) it is possible to create a fully-private guest_memfd for a non-Confidential VM, and even after conversion lands, for both vm_memory_attributes=true and vm_memory_attributes=false.

Quoted 6 lines

In that case, your preservation series can still preserve memory tracked as private by guest_memfd but not used as private, right?

I don't think anyone will use this combination before guest_memfd write() support lands, we just need to make sure there's no kernel crash or corruption in this case.

IIUC, currently, private memory definition is where cocovm with HW based private memory is supported. Which is directly checked by kvm_arch_has_private_mem and this return false incase ARCH does not support it in HW (SEV/SNP, TDX).

So about the combination where a guest_memfd is tracked as private but not actually private. (Created without the INIT_SHARED flags). Even though kvm_arch_has_private_mem is false.

In this case the luo will preserve the guest_memfd. But it will not preserve any attributes, because
1. during creation, we dont populate any attributes, so by default guest_memfd memory always considered to be shared. (Even though no INIT_SHARED is passed). Conversion to private IOCTL will fail as kvm_arch_has_private_mem check will fail for non-COCOVM. for COCOVM, presevation will not be supported and conversion to private memory will be succeded. (No corruption and kernel crash in this case)

==== WHAT IF CONVERSION SERIES LANDS =========

In this case, we have two scenerio
1. kvm->attributes
In this case, Every logic remains same as above.
2. gmem->attributes
if INIT_SHARED: memory is initially shared and no entry in maple tree, and if kvm_arch_has_private_mem returns false (non-COCOVM), this will just fails any conversion request. hence preserving the guest_memfd wont have any problem (fully shared case).

if INIT_SHARED not set: memory is initially private, and there is an entry in maple tree, marked the memory as private. During conversion:
A. To private: it fails as kvm_arch_has_private_mem return false.
Preservation is safe and there will be no problem. as in preservation we dont preserve any attributes, but on retrieval, when a new gmem file is created, identical entry to attributes will also be assigned as INIT_SHARED flag is not set. So we wont have any issues in this case.
B. TO shared: it passes, and update the maple tree. preservation will not be preserving the attributes with current patches, and it will
also allow the preservation as kvm_arch_has_private_mem is false. So
in this case, on retrieval, we will lose the data regarding the
private vs shared (non-cocovm). So if host want to access the shared memory, it will try MMAP and which will fail in new kernel (which was successful in old kernel as conversion happened). But I dont see any kernel crash or corruption.
This is an issue with only non-cocoVM support with conversion series having the gmem attribute enabled without INIT_SHARED flag. I am not sure, if there will be any user for this very soon (is it SW_PROTECTED_VM ?? ).

Let me know, If my understanding is correct. Should we add INIT_SHARED along with kvm_arch_has_private_mem check to make Above case 2.B. impossible in future for the current support of guest_memfd.

#14 lore raw
Quoted 15 lines · [email protected] writes:

Hi,

Quoted 11 lines · Ackerley Tng
Quoted 9 lines · Sean Christopherson
Quoted 8 lines

[...snip...]

we have one open Question left:
1. How to check guest_memfd is fully shared.

[...snip...]

Quoted 8 lines
Quoted 6 lines
Quoted 5 lines

Given that lack of support isn't going to be limited to _just_ guest_memfd, simply disallow preservation if the VM supports private memory:

	if (kvm_arch_has_private_mem(kvm))
		return -EOPNOTSUPP;

This is a good idea, I have implemented it in V2 (sent it recently).

Will look at RFC v2 in a bit :) Thanks!

Quoted 36 lines

Below, I have mentioned a detailed analysis. Please let me know your thoughts.

Quoted 7 lines

Makes sense. Tarun this was the other option that I was suggesting when we discussed offline.

I think (?) it is possible to create a fully-private guest_memfd for a non-Confidential VM, and even after conversion lands, for both vm_memory_attributes=true and vm_memory_attributes=false.

Quoted 6 lines

In that case, your preservation series can still preserve memory tracked as private by guest_memfd but not used as private, right?

I don't think anyone will use this combination before guest_memfd write() support lands, we just need to make sure there's no kernel crash or corruption in this case.

IIUC, currently, private memory definition is where cocovm with HW based private memory is supported. Which is directly checked by kvm_arch_has_private_mem and this return false incase ARCH does not support it in HW (SEV/SNP, TDX).

So about the combination where a guest_memfd is tracked as private but not actually private. (Created without the INIT_SHARED flags). Even though kvm_arch_has_private_mem is false.

In this case the luo will preserve the guest_memfd. But it will not preserve any attributes, because
1. during creation, we dont populate any attributes, so by default guest_memfd memory always considered to be shared. (Even though no INIT_SHARED is passed). Conversion to private IOCTL will fail as kvm_arch_has_private_mem check will fail for non-COCOVM.
for COCOVM,
presevation will not be supported and conversion to private memory will be succeded. (No corruption and kernel crash in this case)

Quoted 2 lines

==== WHAT IF CONVERSION SERIES LANDS =========

I think there are quite many cases to consider if we consider both before and after conversion series lands. For preservation, shall we assume it will go after conversion? :) This way we can avoid discussing some cases.

Quoted 3 lines

In this case, we have two scenerio
1. kvm->attributes
In this case, Every logic remains same as above.

This is the vm_memory_attributes=true case.

Since kvm_supported_mem_attributes(kvm) returns 0 when kvm_arch_has_private_mem(kvm) returns false, the vm's mem_attr_array will always say shared for any vm where kvm_arch_has_private_mem() is false.

So yup, preservation doesn't preserve attributes but that's also effectively preserving always-shared attributes, so all is good.

Quoted 6 lines

2. gmem->attributes
if INIT_SHARED: memory is initially shared and no entry in maple tree, and if kvm_arch_has_private_mem returns false (non-COCOVM), this will just fails any conversion request. hence preserving the guest_memfd wont have any problem (fully shared case).

Yup, preservation doesn't preserve attributes but that's also effectively preserving always-shared attributes, so all is good.

Quoted 7 lines

if INIT_SHARED not set: memory is initially private, and there is an entry in maple tree, marked the memory as private. During conversion:
A. To private: it fails as kvm_arch_has_private_mem return false.
Preservation is safe and there will be no problem. as in preservation we dont preserve any attributes, but on retrieval, when a new gmem file is created, identical entry to attributes will also be assigned as INIT_SHARED flag is not set. So we wont have any issues in this case.

Quoted 8 lines

B. TO shared: it passes, and update the maple tree. preservation will not be preserving the attributes with current patches, and it will also allow the preservation as kvm_arch_has_private_mem is false. So in this case, on retrieval, we will lose the data regarding the private vs shared (non-cocovm). So if host want to access the shared memory, it will try MMAP and which will fail in new kernel (which was successful in old kernel as conversion happened). But I dont see any kernel crash or corruption.

Perhaps it should be this way: the process of preserving should first freeze further conversions, then check for private/shared status, and if private, return error to userspace.

I haven't looked at RFC v2 yet, but IIUC this is kind of in line with the other issue where we freeze allocations:

+ When preserving, freeze further allocations, preserve all pages that are already allocated. If there are pages that weren't allocated, too bad - allocations are frozen until unpreserve.
+ When preserving, freeze further conversions, preserve all shared pages. If there are private pages, fail preservation.

This way, there's no loss of any private/shared state, since only shared pages are preserved.

Quoted 4 lines

This is an issue with only non-cocoVM support with conversion series having the gmem attribute enabled without INIT_SHARED flag. I am not sure, if there will be any user for this very soon (is it SW_PROTECTED_VM ?? ).

So if a non-CoCo VM does use an all-private guest_memfd (didn't specify INIT_SHARED) (don't think anyone will use this before guest_memfd write() support), it'll just always fail preservation unless before preserving everything was converted to shared.

Quoted 3 lines

Let me know, If my understanding is correct. Should we add INIT_SHARED along with kvm_arch_has_private_mem check to make Above case 2.B. impossible in future for the current support of guest_memfd.

#15 lore raw

Hi,

Quoted 20 lines · Ackerley Tng
Quoted 18 lines · [email protected] writes:

Hi,

Quoted 14 lines · Ackerley Tng
Quoted 12 lines · Sean Christopherson
Quoted 8 lines

[...snip...]

we have one open Question left:
1. How to check guest_memfd is fully shared.

[...snip...]

Given that lack of support isn't going to be limited to _just_ guest_memfd, simply disallow preservation if the VM supports private memory:

Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
Quoted 2 lines
	if (kvm_arch_has_private_mem(kvm))
		return -EOPNOTSUPP;

This is a good idea, I have implemented it in V2 (sent it recently).

Will look at RFC v2 in a bit :) Thanks!

Thankyou.

Quoted 36 lines
Quoted 36 lines

Below, I have mentioned a detailed analysis. Please let me know your thoughts.

Quoted 7 lines

Makes sense. Tarun this was the other option that I was suggesting when we discussed offline.

I think (?) it is possible to create a fully-private guest_memfd for a non-Confidential VM, and even after conversion lands, for both vm_memory_attributes=true and vm_memory_attributes=false.

Quoted 6 lines

In that case, your preservation series can still preserve memory tracked as private by guest_memfd but not used as private, right?

I don't think anyone will use this combination before guest_memfd write() support lands, we just need to make sure there's no kernel crash or corruption in this case.

IIUC, currently, private memory definition is where cocovm with HW based private memory is supported. Which is directly checked by kvm_arch_has_private_mem and this return false incase ARCH does not support it in HW (SEV/SNP, TDX).

So about the combination where a guest_memfd is tracked as private but not actually private. (Created without the INIT_SHARED flags). Even though kvm_arch_has_private_mem is false.

In this case the luo will preserve the guest_memfd. But it will not preserve any attributes, because
1. during creation, we dont populate any attributes, so by default guest_memfd memory always considered to be shared. (Even though no INIT_SHARED is passed). Conversion to private IOCTL will fail as kvm_arch_has_private_mem check will fail for non-COCOVM.
for COCOVM,
presevation will not be supported and conversion to private memory will be succeded. (No corruption and kernel crash in this case)

Quoted 8 lines
Quoted 2 lines

==== WHAT IF CONVERSION SERIES LANDS =========

I think there are quite many cases to consider if we consider both before and after conversion series lands. For preservation, shall we assume it will go after conversion? :) This way we can avoid discussing some cases.

No, I dont prefer to make any assumption and try to make both the series independent of each other. Landing any of the series first should not break another. I am keeping this goal.

Quoted 44 lines
Quoted 3 lines

In this case, we have two scenerio
1. kvm->attributes
In this case, Every logic remains same as above.

This is the vm_memory_attributes=true case.

Since kvm_supported_mem_attributes(kvm) returns 0 when kvm_arch_has_private_mem(kvm) returns false, the vm's mem_attr_array will always say shared for any vm where kvm_arch_has_private_mem() is false.

So yup, preservation doesn't preserve attributes but that's also effectively preserving always-shared attributes, so all is good.

Quoted 6 lines

2. gmem->attributes
if INIT_SHARED: memory is initially shared and no entry in maple tree, and if kvm_arch_has_private_mem returns false (non-COCOVM), this will just fails any conversion request. hence preserving the guest_memfd wont have any problem (fully shared case).

Yup, preservation doesn't preserve attributes but that's also effectively preserving always-shared attributes, so all is good.

Quoted 7 lines

if INIT_SHARED not set: memory is initially private, and there is an entry in maple tree, marked the memory as private. During conversion:
A. To private: it fails as kvm_arch_has_private_mem return false.
Preservation is safe and there will be no problem. as in preservation we dont preserve any attributes, but on retrieval, when a new gmem file is created, identical entry to attributes will also be assigned as INIT_SHARED flag is not set. So we wont have any issues in this case.

Quoted 8 lines

B. TO shared: it passes, and update the maple tree. preservation will not be preserving the attributes with current patches, and it will also allow the preservation as kvm_arch_has_private_mem is false. So in this case, on retrieval, we will lose the data regarding the private vs shared (non-cocovm). So if host want to access the shared memory, it will try MMAP and which will fail in new kernel (which was successful in old kernel as conversion happened). But I dont see any kernel crash or corruption.

Perhaps it should be this way: the process of preserving should first freeze further conversions, then check for private/shared status, and if private, return error to userspace.

Quoted 9 lines

I haven't looked at RFC v2 yet, but IIUC this is kind of in line with the other issue where we freeze allocations:

+ When preserving, freeze further allocations, preserve all pages that are already allocated. If there are pages that weren't allocated, too bad - allocations are frozen until unpreserve.
+ When preserving, freeze further conversions, preserve all shared pages. If there are private pages, fail preservation.

For this series, I am strictly focusing on fully shared guest_memfd. And any guest_memfd that supports or will support conversion to private will not be allowed for preservation with this series.

Freezing the conversion, is requirement for guest_memfd that will support private mapping. At this point I dont want to include this in this base support. We will look on this when we will work on preservation of private mapping.

Quoted 8 lines

This way, there's no loss of any private/shared state, since only shared pages are preserved.

Quoted 4 lines

This is an issue with only non-cocoVM support with conversion series having the gmem attribute enabled without INIT_SHARED flag. I am not sure, if there will be any user for this very soon (is it SW_PROTECTED_VM ?? ).

Quoted 5 lines

So if a non-CoCo VM does use an all-private guest_memfd (didn't specify INIT_SHARED) (don't think anyone will use this before guest_memfd write() support), it'll just always fail preservation unless before preserving everything was converted to shared.

yes, Also
We will never know if everything is converted to shared unless we walk the maple tree, But for this base support, we dont want to depend on conversion series, So depending on INIT_SHARED flag along with kvm_arch_has_private_mem check makes this series immune to conversion series, INIT_SHARED makes the initial mapping shared, and kvm_arch_has_private_mem avoid any further conversion which is also true before and after conversion series.

WDYT? V2 does not have INIT_SHARED check, it has only kvm_arch_has_private_mem. I can comment there to be updated in next version, if we are aligned on this.

Thanks.