|
| 1 | +From 9a4d7cd855d14e1522f363e3e04ebb9fa0a90ff0 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Fuad Tabba <tabba@google.com> |
| 3 | +Date: Tue, 18 Mar 2025 16:18:16 +0000 |
| 4 | +Subject: [PATCH 02/34] KVM: guest_memfd: Handle final folio_put() of |
| 5 | + guest_memfd pages |
| 6 | + |
| 7 | +Before transitioning a guest_memfd folio to unshared, thereby |
| 8 | +disallowing access by the host and allowing the hypervisor to |
| 9 | +transition its view of the guest page as private, we need to be |
| 10 | +sure that the host doesn't have any references to the folio. |
| 11 | + |
| 12 | +This patch introduces a new type for guest_memfd folios, which |
| 13 | +isn't activated in this series but is here as a placeholder and |
| 14 | +to facilitate the code in the subsequent patch series. This will |
| 15 | +be used in the future to register a callback that informs the |
| 16 | +guest_memfd subsystem when the last reference is dropped, |
| 17 | +therefore knowing that the host doesn't have any remaining |
| 18 | +references. |
| 19 | + |
| 20 | +This patch also introduces the configuration option, |
| 21 | +KVM_GMEM_SHARED_MEM, which toggles support for mapping |
| 22 | +guest_memfd shared memory at the host. |
| 23 | + |
| 24 | +Signed-off-by: Fuad Tabba <tabba@google.com> |
| 25 | +Acked-by: Vlastimil Babka <vbabka@suse.cz> |
| 26 | +Acked-by: David Hildenbrand <david@redhat.com> |
| 27 | +--- |
| 28 | + include/linux/kvm_host.h | 4 ++++ |
| 29 | + include/linux/page-flags.h | 16 ++++++++++++++++ |
| 30 | + mm/debug.c | 1 + |
| 31 | + mm/swap.c | 29 +++++++++++++++++++++++++++++ |
| 32 | + virt/kvm/Kconfig | 4 ++++ |
| 33 | + virt/kvm/guest_memfd.c | 8 ++++++++ |
| 34 | + 6 files changed, 62 insertions(+) |
| 35 | + |
| 36 | +diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h |
| 37 | +index f34f4cfaa513..3ad0719bfc4f 100644 |
| 38 | +--- a/include/linux/kvm_host.h |
| 39 | ++++ b/include/linux/kvm_host.h |
| 40 | +@@ -2571,4 +2571,8 @@ long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu, |
| 41 | + struct kvm_pre_fault_memory *range); |
| 42 | + #endif |
| 43 | + |
| 44 | ++#ifdef CONFIG_KVM_GMEM_SHARED_MEM |
| 45 | ++void kvm_gmem_handle_folio_put(struct folio *folio); |
| 46 | ++#endif |
| 47 | ++ |
| 48 | + #endif |
| 49 | +diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h |
| 50 | +index 6dc2494bd002..daeee9a38e4c 100644 |
| 51 | +--- a/include/linux/page-flags.h |
| 52 | ++++ b/include/linux/page-flags.h |
| 53 | +@@ -933,6 +933,7 @@ enum pagetype { |
| 54 | + PGTY_slab = 0xf5, |
| 55 | + PGTY_zsmalloc = 0xf6, |
| 56 | + PGTY_unaccepted = 0xf7, |
| 57 | ++ PGTY_guestmem = 0xf8, |
| 58 | + |
| 59 | + PGTY_mapcount_underflow = 0xff |
| 60 | + }; |
| 61 | +@@ -1082,6 +1083,21 @@ FOLIO_TYPE_OPS(hugetlb, hugetlb) |
| 62 | + FOLIO_TEST_FLAG_FALSE(hugetlb) |
| 63 | + #endif |
| 64 | + |
| 65 | ++/* |
| 66 | ++ * guestmem folios are used to back VM memory as managed by guest_memfd. Once |
| 67 | ++ * the last reference is put, instead of freeing these folios back to the page |
| 68 | ++ * allocator, they are returned to guest_memfd. |
| 69 | ++ * |
| 70 | ++ * For now, guestmem will only be set on these folios as long as they cannot be |
| 71 | ++ * mapped to user space ("private state"), with the plan of always setting that |
| 72 | ++ * type once typed folios can be mapped to user space cleanly. |
| 73 | ++ */ |
| 74 | ++#ifdef CONFIG_KVM_GMEM_SHARED_MEM |
| 75 | ++FOLIO_TYPE_OPS(guestmem, guestmem) |
| 76 | ++#else |
| 77 | ++FOLIO_TEST_FLAG_FALSE(guestmem) |
| 78 | ++#endif |
| 79 | ++ |
| 80 | + PAGE_TYPE_OPS(Zsmalloc, zsmalloc, zsmalloc) |
| 81 | + |
| 82 | + /* |
| 83 | +diff --git a/mm/debug.c b/mm/debug.c |
| 84 | +index 8d2acf432385..08bc42c6cba8 100644 |
| 85 | +--- a/mm/debug.c |
| 86 | ++++ b/mm/debug.c |
| 87 | +@@ -56,6 +56,7 @@ static const char *page_type_names[] = { |
| 88 | + DEF_PAGETYPE_NAME(table), |
| 89 | + DEF_PAGETYPE_NAME(buddy), |
| 90 | + DEF_PAGETYPE_NAME(unaccepted), |
| 91 | ++ DEF_PAGETYPE_NAME(guestmem), |
| 92 | + }; |
| 93 | + |
| 94 | + static const char *page_type_name(unsigned int page_type) |
| 95 | +diff --git a/mm/swap.c b/mm/swap.c |
| 96 | +index 47bc1bb919cc..d8fda3948684 100644 |
| 97 | +--- a/mm/swap.c |
| 98 | ++++ b/mm/swap.c |
| 99 | +@@ -38,6 +38,10 @@ |
| 100 | + #include <linux/local_lock.h> |
| 101 | + #include <linux/buffer_head.h> |
| 102 | + |
| 103 | ++#ifdef CONFIG_KVM_GMEM_SHARED_MEM |
| 104 | ++#include <linux/kvm_host.h> |
| 105 | ++#endif |
| 106 | ++ |
| 107 | + #include "internal.h" |
| 108 | + |
| 109 | + #define CREATE_TRACE_POINTS |
| 110 | +@@ -94,6 +98,26 @@ static void page_cache_release(struct folio *folio) |
| 111 | + unlock_page_lruvec_irqrestore(lruvec, flags); |
| 112 | + } |
| 113 | + |
| 114 | ++#ifdef CONFIG_KVM_GMEM_SHARED_MEM |
| 115 | ++static void gmem_folio_put(struct folio *folio) |
| 116 | ++{ |
| 117 | ++ /* |
| 118 | ++ * Perform the callback only as long as the KVM module is still loaded. |
| 119 | ++ * As long as the folio mapping is set, the folio is associated with a |
| 120 | ++ * guest_memfd inode. |
| 121 | ++ */ |
| 122 | ++ if (folio->mapping) |
| 123 | ++ kvm_gmem_handle_folio_put(folio); |
| 124 | ++ |
| 125 | ++ /* |
| 126 | ++ * If there are no references to the folio left, it's not associated |
| 127 | ++ * with a guest_memfd inode anymore. |
| 128 | ++ */ |
| 129 | ++ if (folio_ref_count(folio) == 0) |
| 130 | ++ __folio_put(folio); |
| 131 | ++} |
| 132 | ++#endif /* CONFIG_KVM_GMEM_SHARED_MEM */ |
| 133 | ++ |
| 134 | + static void free_typed_folio(struct folio *folio) |
| 135 | + { |
| 136 | + switch (folio_get_type(folio)) { |
| 137 | +@@ -101,6 +125,11 @@ static void free_typed_folio(struct folio *folio) |
| 138 | + case PGTY_hugetlb: |
| 139 | + free_huge_folio(folio); |
| 140 | + return; |
| 141 | ++#endif |
| 142 | ++#ifdef CONFIG_KVM_GMEM_SHARED_MEM |
| 143 | ++ case PGTY_guestmem: |
| 144 | ++ gmem_folio_put(folio); |
| 145 | ++ return; |
| 146 | + #endif |
| 147 | + default: |
| 148 | + WARN_ON_ONCE(1); |
| 149 | +diff --git a/virt/kvm/Kconfig b/virt/kvm/Kconfig |
| 150 | +index 54e959e7d68f..4e759e8020c5 100644 |
| 151 | +--- a/virt/kvm/Kconfig |
| 152 | ++++ b/virt/kvm/Kconfig |
| 153 | +@@ -124,3 +124,7 @@ config HAVE_KVM_ARCH_GMEM_PREPARE |
| 154 | + config HAVE_KVM_ARCH_GMEM_INVALIDATE |
| 155 | + bool |
| 156 | + depends on KVM_PRIVATE_MEM |
| 157 | ++ |
| 158 | ++config KVM_GMEM_SHARED_MEM |
| 159 | ++ select KVM_PRIVATE_MEM |
| 160 | ++ bool |
| 161 | +diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c |
| 162 | +index b2aa6bf24d3a..5fc414becae5 100644 |
| 163 | +--- a/virt/kvm/guest_memfd.c |
| 164 | ++++ b/virt/kvm/guest_memfd.c |
| 165 | +@@ -13,6 +13,14 @@ struct kvm_gmem { |
| 166 | + struct list_head entry; |
| 167 | + }; |
| 168 | + |
| 169 | ++#ifdef CONFIG_KVM_GMEM_SHARED_MEM |
| 170 | ++void kvm_gmem_handle_folio_put(struct folio *folio) |
| 171 | ++{ |
| 172 | ++ WARN_ONCE(1, "A placeholder that shouldn't trigger. Work in progress."); |
| 173 | ++} |
| 174 | ++EXPORT_SYMBOL_GPL(kvm_gmem_handle_folio_put); |
| 175 | ++#endif /* CONFIG_KVM_GMEM_SHARED_MEM */ |
| 176 | ++ |
| 177 | + /** |
| 178 | + * folio_file_pfn - like folio_file_page, but return a pfn. |
| 179 | + * @folio: The folio which contains this index. |
| 180 | +-- |
| 181 | +2.47.1 |
| 182 | + |
0 commit comments