From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7624FC433EF for ; Wed, 24 Nov 2021 13:17:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1347860AbhKXNVF (ORCPT ); Wed, 24 Nov 2021 08:21:05 -0500 Received: from mail.kernel.org ([198.145.29.99]:36592 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1346667AbhKXNRu (ORCPT ); Wed, 24 Nov 2021 08:17:50 -0500 Received: by mail.kernel.org (Postfix) with ESMTPSA id 2C65B60F5D; Wed, 24 Nov 2021 12:45:32 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=linuxfoundation.org; s=korg; t=1637757933; bh=qXO9zL7DKiI5S+DRKbWnkzpbkmi8Mg1ocP4lt6h76Jw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=T8rolWEGYJPNdmEID/mFcHEUwKEWPaaTUQClaU7tR7LMO6m8uM4vIS1x2PHDlCGys +nDQ4x5C/w9pyerDUf4P/uZumXRw/+MsnJtQlZC9x9FHt1fYDte+LgtphWeYTP1Mxa nHSMHibV8yUV0U5On0B/TpvKdA+CeRpiYQ43Ab6U= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Nadav Amit , Mike Kravetz , "Aneesh Kumar K.V" , KAMEZAWA Hiroyuki , Andrew Morton , Linus Torvalds Subject: [PATCH 4.19 323/323] hugetlbfs: flush TLBs correctly after huge_pmd_unshare Date: Wed, 24 Nov 2021 12:58:33 +0100 Message-Id: <20211124115729.826426341@linuxfoundation.org> X-Mailer: git-send-email 2.34.0 In-Reply-To: <20211124115718.822024889@linuxfoundation.org> References: <20211124115718.822024889@linuxfoundation.org> User-Agent: quilt/0.66 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 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 | 6 ++++++ mm/hugetlb.c | 23 +++++++++++++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) --- a/include/asm-generic/tlb.h +++ b/include/asm-generic/tlb.h @@ -205,6 +205,12 @@ static inline void tlb_remove_check_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) +{ + __tlb_adjust_range(tlb, address, 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 @@ -3425,6 +3425,7 @@ void __unmap_hugepage_range(struct mmu_g unsigned long sz = huge_page_size(h); unsigned long mmun_start = start; /* For mmu_notifiers */ 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)); @@ -3451,10 +3452,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); - /* - * We just unmapped a page of PMDs by clearing a PUD. - * The caller's TLB flush range should cover this area. - */ + tlb_flush_pmd_range(tlb, address & PUD_MASK, PUD_SIZE); + force_flush = true; continue; } @@ -3511,6 +3510,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_tlbonly(tlb); } void __unmap_hugepage_range_final(struct mmu_gather *tlb,