History

mm: thp: KVM: Explicitly check for THP when populating secondary MMU

Add a helper, is_transparent_hugepage(), to explicitly check whether a compound page is a THP and use it when populating KVM's secondary MMU. The explicit check fixes a bug where a remapped compound page, e.g. for an XDP Rx socket, is mapped into a KVM guest and is mistaken for a THP, which results in KVM incorrectly creating a huge page in its secondary MMU.

Sean Christopherson committed 6 years ago

Commit 005ba371 parent 22b1d576 files · +31 −9Browse files

Unified · Split

6 files
  1. arch/x86/kvm/mmu/mmu.c +2 −2
  2. include/linux/huge_mm.h +6 −0
  3. include/linux/kvm_host.h +1 −0
  4. mm/huge_memory.c +11 −0
  5. virt/kvm/arm/mmu.c +1 −7
  6. virt/kvm/kvm_main.c +10 −0
arch/x86/kvm/mmu/mmu.c+2−2
@@ -3344,7 +3344,7 @@ static void transparent_hugepage_adjust(struct kvm_vcpu *vcpu, · 3343 unmodified lines
33443344 */
33453345 if (!is_error_noslot_pfn(pfn) && !kvm_is_reserved_pfn(pfn) &&
33463346 !kvm_is_zone_device_pfn(pfn) && level == PT_PAGE_TABLE_LEVEL &&
3347− PageTransCompoundMap(pfn_to_page(pfn))) {
3347+ kvm_is_transparent_hugepage(pfn)) {
33483348 unsigned long mask;
33493349
33503350 /*
@@ -5961,7 +5961,7 @@ restart: · 2610 unmodified lines
59615961 */
59625962 if (sp->role.direct && !kvm_is_reserved_pfn(pfn) &&
59635963 !kvm_is_zone_device_pfn(pfn) &&
5964− PageTransCompoundMap(pfn_to_page(pfn))) {
5964+ kvm_is_transparent_hugepage(pfn)) {
59655965 pte_list_remove(rmap_head, sptep);
59665966
59675967 if (kvm_available_flush_tlb_with_range())
include/linux/huge_mm.h+6−0
@@ -160,6 +160,7 @@ extern unsigned long thp_get_unmapped_area(struct file *filp, · 159 unmodified lines
160160
161161 extern void prep_transhuge_page(struct page *page);
162162 extern void free_transhuge_page(struct page *page);
163+bool is_transparent_hugepage(struct page *page);
163164
164165 bool can_split_huge_page(struct page *page, int *pextra_pins);
165166 int split_huge_page_to_list(struct page *page, struct list_head *list);
@@ -308,6 +309,11 @@ static inline bool transhuge_vma_suitable(struct vm_area_struct *vma, · 142 unmodified lines
308309
309310 static inline void prep_transhuge_page(struct page *page) {}
310311
312+static inline bool is_transparent_hugepage(struct page *page)
313+{
314+ return false;
315+}
316+
311317 #define transparent_hugepage_flags 0UL
312318
313319 #define thp_get_unmapped_area NULL
include/linux/kvm_host.h+1−0
@@ -976,6 +976,7 @@ int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu); · 975 unmodified lines
976976
977977 bool kvm_is_reserved_pfn(kvm_pfn_t pfn);
978978 bool kvm_is_zone_device_pfn(kvm_pfn_t pfn);
979+bool kvm_is_transparent_hugepage(kvm_pfn_t pfn);
979980
980981 struct kvm_irq_ack_notifier {
981982 struct hlist_node link;
mm/huge_memory.c+11−0
@@ -527,6 +527,17 @@ void prep_transhuge_page(struct page *page) · 526 unmodified lines
527527 set_compound_page_dtor(page, TRANSHUGE_PAGE_DTOR);
528528 }
529529
530+bool is_transparent_hugepage(struct page *page)
531+{
532+ if (!PageCompound(page))
533+ return 0;
534+
535+ page = compound_head(page);
536+ return is_huge_zero_page(page) ||
537+ page[1].compound_dtor == TRANSHUGE_PAGE_DTOR;
538+}
539+EXPORT_SYMBOL_GPL(is_transparent_hugepage);
540+
530541 static unsigned long __thp_get_unmapped_area(struct file *filp, unsigned long len,
531542 loff_t off, unsigned long flags, unsigned long size)
532543 {
virt/kvm/arm/mmu.c+1−7
@@ -1377,14 +1377,8 @@ static bool transparent_hugepage_adjust(kvm_pfn_t *pfnp, phys_addr_t *ipap) · 1376 unmodified lines
13771377 {
13781378 kvm_pfn_t pfn = *pfnp;
13791379 gfn_t gfn = *ipap >> PAGE_SHIFT;
1380− struct page *page = pfn_to_page(pfn);
13811380
1382− /*
1383− * PageTransCompoundMap() returns true for THP and
1384− * hugetlbfs. Make sure the adjustment is done only for THP
1385− * pages.
1386− */
1387− if (!PageHuge(page) && PageTransCompoundMap(page)) {
1381+ if (kvm_is_transparent_hugepage(pfn)) {
13881382 unsigned long mask;
13891383 /*
13901384 * The address we faulted on is backed by a transparent huge
virt/kvm/kvm_main.c+10−0
@@ -191,6 +191,16 @@ bool kvm_is_reserved_pfn(kvm_pfn_t pfn) · 190 unmodified lines
191191 return true;
192192 }
193193
194+bool kvm_is_transparent_hugepage(kvm_pfn_t pfn)
195+{
196+ struct page *page = pfn_to_page(pfn);
197+
198+ if (!PageTransCompoundMap(page))
199+ return false;
200+
201+ return is_transparent_hugepage(compound_head(page));
202+}
203+
194204 /*
195205 * Switches to specified vcpu, until a matching vcpu_put()
196206 */