aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>2016-12-12 19:41:56 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-01-12 05:39:32 -0500
commit66c6770379368ba0738a61c2317edc0ca324b902 (patch)
tree152839d3c5cc5f1fb3849d8e1ea2d994557188ce
parent17df3e74fb5174bc9fc078fd640dc3b2762f0ca1 (diff)
mm/hugetlb.c: use the right pte val for compare in hugetlb_cow
commit 3999f52e3198e76607446ab1a4610c1ddc406c56 upstream. We cannot use the pte value used in set_pte_at for pte_same comparison, because archs like ppc64, filter/add new pte flag in set_pte_at. Instead fetch the pte value inside hugetlb_cow. We are comparing pte value to make sure the pte didn't change since we dropped the page table lock. hugetlb_cow get called with page table lock held, and we can take a copy of the pte value before we drop the page table lock. With hugetlbfs, we optimize the MAP_PRIVATE write fault path with no previous mapping (huge_pte_none entries), by forcing a cow in the fault path. This avoid take an addition fault to covert a read-only mapping to read/write. Here we were comparing a recently instantiated pte (via set_pte_at) to the pte values from linux page table. As explained above on ppc64 such pte_same check returned wrong result, resulting in us taking an additional fault on ppc64. Fixes: 6a119eae942c ("powerpc/mm: Add a _PAGE_PTE bit") Link: http://lkml.kernel.org/r/20161018154245.18023-1-aneesh.kumar@linux.vnet.ibm.com Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> Reported-by: Jan Stancek <jstancek@redhat.com> Acked-by: Hillf Danton <hillf.zj@alibaba-inc.com> Cc: Mike Kravetz <mike.kravetz@oracle.com> Cc: Scott Wood <scottwood@freescale.com> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--mm/hugetlb.c12
1 files changed, 7 insertions, 5 deletions
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 418bf01a50ed..23aec01836aa 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -3450,15 +3450,17 @@ static void unmap_ref_private(struct mm_struct *mm, struct vm_area_struct *vma,
3450 * Keep the pte_same checks anyway to make transition from the mutex easier. 3450 * Keep the pte_same checks anyway to make transition from the mutex easier.
3451 */ 3451 */
3452static int hugetlb_cow(struct mm_struct *mm, struct vm_area_struct *vma, 3452static int hugetlb_cow(struct mm_struct *mm, struct vm_area_struct *vma,
3453 unsigned long address, pte_t *ptep, pte_t pte, 3453 unsigned long address, pte_t *ptep,
3454 struct page *pagecache_page, spinlock_t *ptl) 3454 struct page *pagecache_page, spinlock_t *ptl)
3455{ 3455{
3456 pte_t pte;
3456 struct hstate *h = hstate_vma(vma); 3457 struct hstate *h = hstate_vma(vma);
3457 struct page *old_page, *new_page; 3458 struct page *old_page, *new_page;
3458 int ret = 0, outside_reserve = 0; 3459 int ret = 0, outside_reserve = 0;
3459 unsigned long mmun_start; /* For mmu_notifiers */ 3460 unsigned long mmun_start; /* For mmu_notifiers */
3460 unsigned long mmun_end; /* For mmu_notifiers */ 3461 unsigned long mmun_end; /* For mmu_notifiers */
3461 3462
3463 pte = huge_ptep_get(ptep);
3462 old_page = pte_page(pte); 3464 old_page = pte_page(pte);
3463 3465
3464retry_avoidcopy: 3466retry_avoidcopy:
@@ -3733,7 +3735,7 @@ retry:
3733 hugetlb_count_add(pages_per_huge_page(h), mm); 3735 hugetlb_count_add(pages_per_huge_page(h), mm);
3734 if ((flags & FAULT_FLAG_WRITE) && !(vma->vm_flags & VM_SHARED)) { 3736 if ((flags & FAULT_FLAG_WRITE) && !(vma->vm_flags & VM_SHARED)) {
3735 /* Optimization, do the COW without a second fault */ 3737 /* Optimization, do the COW without a second fault */
3736 ret = hugetlb_cow(mm, vma, address, ptep, new_pte, page, ptl); 3738 ret = hugetlb_cow(mm, vma, address, ptep, page, ptl);
3737 } 3739 }
3738 3740
3739 spin_unlock(ptl); 3741 spin_unlock(ptl);
@@ -3888,8 +3890,8 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma,
3888 3890
3889 if (flags & FAULT_FLAG_WRITE) { 3891 if (flags & FAULT_FLAG_WRITE) {
3890 if (!huge_pte_write(entry)) { 3892 if (!huge_pte_write(entry)) {
3891 ret = hugetlb_cow(mm, vma, address, ptep, entry, 3893 ret = hugetlb_cow(mm, vma, address, ptep,
3892 pagecache_page, ptl); 3894 pagecache_page, ptl);
3893 goto out_put_page; 3895 goto out_put_page;
3894 } 3896 }
3895 entry = huge_pte_mkdirty(entry); 3897 entry = huge_pte_mkdirty(entry);