From: Greg Kroah-Hartman Date: Wed, 24 Nov 2021 11:14:30 +0000 (+0100) Subject: 4.9-stable patches X-Git-Tag: v5.15.5~17 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=f7e42efd397782736a20fb83c92207dbbb2a4f52;p=thirdparty%2Fkernel%2Fstable-queue.git 4.9-stable patches added patches: hugetlbfs-flush-tlbs-correctly-after-huge_pmd_unshare.patch --- diff --git a/queue-4.9/hugetlbfs-flush-tlbs-correctly-after-huge_pmd_unshare.patch b/queue-4.9/hugetlbfs-flush-tlbs-correctly-after-huge_pmd_unshare.patch new file mode 100644 index 00000000000..0392fc7637e --- /dev/null +++ b/queue-4.9/hugetlbfs-flush-tlbs-correctly-after-huge_pmd_unshare.patch @@ -0,0 +1,107 @@ +From a4a118f2eead1d6c49e00765de89878288d4b890 Mon Sep 17 00:00:00 2001 +From: Nadav Amit +Date: Sun, 21 Nov 2021 12:40:07 -0800 +Subject: hugetlbfs: flush TLBs correctly after huge_pmd_unshare + +From: Nadav Amit + +commit a4a118f2eead1d6c49e00765de89878288d4b890 upstream. + +When __unmap_hugepage_range() calls to huge_pmd_unshare() succeed, a TLB +flush is missing. This TLB flush must be performed before releasing the +i_mmap_rwsem, in order to prevent an unshared PMDs page from being +released and reused before the TLB flush took place. + +Arguably, a comprehensive solution would use mmu_gather interface to +batch the TLB flushes and the PMDs page release, however it is not an +easy solution: (1) try_to_unmap_one() and try_to_migrate_one() also call +huge_pmd_unshare() and they cannot use the mmu_gather interface; and (2) +deferring the release of the page reference for the PMDs page until +after i_mmap_rwsem is dropeed can confuse huge_pmd_unshare() into +thinking PMDs are shared when they are not. + +Fix __unmap_hugepage_range() by adding the missing TLB flush, and +forcing a flush when unshare is successful. + +Fixes: 24669e58477e ("hugetlb: use mmu_gather instead of a temporary linked list for accumulating pages)" # 3.6 +Signed-off-by: Nadav Amit +Reviewed-by: Mike Kravetz +Cc: Aneesh Kumar K.V +Cc: KAMEZAWA Hiroyuki +Cc: Andrew Morton +Signed-off-by: Linus Torvalds +Signed-off-by: Greg Kroah-Hartman + +--- + include/asm-generic/tlb.h | 17 +++++++++++++++++ + mm/hugetlb.c | 19 +++++++++++++++++++ + 2 files changed, 36 insertions(+) + +--- a/include/asm-generic/tlb.h ++++ b/include/asm-generic/tlb.h +@@ -202,6 +202,23 @@ static inline bool __tlb_remove_pte_page + #define tlb_end_vma __tlb_end_vma + #endif + ++static inline void tlb_flush_pmd_range(struct mmu_gather *tlb, ++ unsigned long address, unsigned long size) ++{ ++ if (tlb->page_size != 0 && tlb->page_size != PMD_SIZE) ++ tlb_flush_mmu(tlb); ++ ++ tlb->page_size = PMD_SIZE; ++ tlb->start = min(tlb->start, address); ++ tlb->end = max(tlb->end, address + size); ++ /* ++ * Track the last address with which we adjusted the range. This ++ * will be used later to adjust again after a mmu_flush due to ++ * failed __tlb_remove_page ++ */ ++ tlb->addr = address + size - PMD_SIZE; ++} ++ + #ifndef __tlb_remove_tlb_entry + #define __tlb_remove_tlb_entry(tlb, ptep, address) do { } while (0) + #endif +--- a/mm/hugetlb.c ++++ b/mm/hugetlb.c +@@ -3395,6 +3395,7 @@ void __unmap_hugepage_range(struct mmu_g + unsigned long sz = huge_page_size(h); + const unsigned long mmun_start = start; /* For mmu_notifiers */ + const unsigned long mmun_end = end; /* For mmu_notifiers */ ++ bool force_flush = false; + + WARN_ON(!is_vm_hugetlb_page(vma)); + BUG_ON(start & ~huge_page_mask(h)); +@@ -3411,6 +3412,8 @@ void __unmap_hugepage_range(struct mmu_g + ptl = huge_pte_lock(h, mm, ptep); + if (huge_pmd_unshare(mm, &address, ptep)) { + spin_unlock(ptl); ++ tlb_flush_pmd_range(tlb, address & PUD_MASK, PUD_SIZE); ++ force_flush = true; + continue; + } + +@@ -3467,6 +3470,22 @@ void __unmap_hugepage_range(struct mmu_g + } + mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end); + tlb_end_vma(tlb, vma); ++ ++ /* ++ * If we unshared PMDs, the TLB flush was not recorded in mmu_gather. We ++ * could defer the flush until now, since by holding i_mmap_rwsem we ++ * guaranteed that the last refernece would not be dropped. But we must ++ * do the flushing before we return, as otherwise i_mmap_rwsem will be ++ * dropped and the last reference to the shared PMDs page might be ++ * dropped as well. ++ * ++ * In theory we could defer the freeing of the PMD pages as well, but ++ * huge_pmd_unshare() relies on the exact page_count for the PMD page to ++ * detect sharing, so we cannot defer the release of the page either. ++ * Instead, do flush now. ++ */ ++ if (force_flush) ++ tlb_flush_mmu(tlb); + } + + void __unmap_hugepage_range_final(struct mmu_gather *tlb, diff --git a/queue-4.9/series b/queue-4.9/series index 0904a21f5bb..6c09daa2260 100644 --- a/queue-4.9/series +++ b/queue-4.9/series @@ -204,3 +204,4 @@ batman-adv-reserve-needed_-room-for-fragments.patch batman-adv-don-t-always-reallocate-the-fragmentation-skb-head.patch asoc-dapm-cover-regression-by-kctl-change-notification-fix.patch usb-max-3421-use-driver-data-instead-of-maintaining-a-list-of-bound-devices.patch +hugetlbfs-flush-tlbs-correctly-after-huge_pmd_unshare.patch