All of lore.kernel.org
 help / color / mirror / Atom feed
From: Sasha Levin <sashal@kernel.org>
To: stable@vger.kernel.org, linux-kernel@vger.kernel.org
Cc: Andrea Arcangeli <aarcange@redhat.com>,
	Jerome Glisse <jglisse@redhat.com>,
	Andrew Morton <akpm@linux-foundation.org>,
	Linus Torvalds <torvalds@linux-foundation.org>,
	Sasha Levin <sashal@kernel.org>
Subject: [PATCH AUTOSEL 4.19 02/57] mm: thp: fix mmu_notifier in migrate_misplaced_transhuge_page()
Date: Sun,  4 Nov 2018 08:50:49 -0500	[thread overview]
Message-ID: <20181104135144.88324-2-sashal@kernel.org> (raw)
In-Reply-To: <20181104135144.88324-1-sashal@kernel.org>

From: Andrea Arcangeli <aarcange@redhat.com>

[ Upstream commit 7066f0f933a1fd707bb38781866657769cff7efc ]

change_huge_pmd() after arming the numa/protnone pmd doesn't flush the TLB
right away.  do_huge_pmd_numa_page() flushes the TLB before calling
migrate_misplaced_transhuge_page().  By the time do_huge_pmd_numa_page()
runs some CPU could still access the page through the TLB.

change_huge_pmd() before arming the numa/protnone transhuge pmd calls
mmu_notifier_invalidate_range_start().  So there's no need of
mmu_notifier_invalidate_range_start()/mmu_notifier_invalidate_range_only_end()
sequence in migrate_misplaced_transhuge_page() too, because by the time
migrate_misplaced_transhuge_page() runs, the pmd mapping has already been
invalidated in the secondary MMUs.  It has to or if a secondary MMU can
still write to the page, the migrate_page_copy() would lose data.

However an explicit mmu_notifier_invalidate_range() is needed before
migrate_misplaced_transhuge_page() starts copying the data of the
transhuge page or the below can happen for MMU notifier users sharing the
primary MMU pagetables and only implementing ->invalidate_range:

CPU0		CPU1		GPU sharing linux pagetables using
                                only ->invalidate_range
-----------	------------	---------
				GPU secondary MMU writes to the page
				mapped by the transhuge pmd
change_pmd_range()
mmu..._range_start()
->invalidate_range_start() noop
change_huge_pmd()
set_pmd_at(numa/protnone)
pmd_unlock()
		do_huge_pmd_numa_page()
		CPU TLB flush globally (1)
		CPU cannot write to page
		migrate_misplaced_transhuge_page()
				GPU writes to the page...
		migrate_page_copy()
				...GPU stops writing to the page
CPU TLB flush (2)
mmu..._range_end() (3)
->invalidate_range_stop() noop
->invalidate_range()
				GPU secondary MMU is invalidated
				and cannot write to the page anymore
				(too late)

Just like we need a CPU TLB flush (1) because the TLB flush (2) arrives
too late, we also need a mmu_notifier_invalidate_range() before calling
migrate_misplaced_transhuge_page(), because the ->invalidate_range() in
(3) also arrives too late.

This requirement is the result of the lazy optimization in
change_huge_pmd() that releases the pmd_lock without first flushing the
TLB and without first calling mmu_notifier_invalidate_range().

Even converting the removed mmu_notifier_invalidate_range_only_end() into
a mmu_notifier_invalidate_range_end() would not have been enough to fix
this, because it run after migrate_page_copy().

After the hugepage data copy is done migrate_misplaced_transhuge_page()
can proceed and call set_pmd_at without having to flush the TLB nor any
secondary MMUs because the secondary MMU invalidate, just like the CPU TLB
flush, has to happen before the migrate_page_copy() is called or it would
be a bug in the first place (and it was for drivers using
->invalidate_range()).

KVM is unaffected because it doesn't implement ->invalidate_range().

The standard PAGE_SIZEd migrate_misplaced_page is less accelerated and
uses the generic migrate_pages which transitions the pte from
numa/protnone to a migration entry in try_to_unmap_one() and flushes TLBs
and all mmu notifiers there before copying the page.

Link: http://lkml.kernel.org/r/20181013002430.698-3-aarcange@redhat.com
Signed-off-by: Andrea Arcangeli <aarcange@redhat.com>
Acked-by: Mel Gorman <mgorman@suse.de>
Acked-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Reviewed-by: Aaron Tomlin <atomlin@redhat.com>
Cc: Jerome Glisse <jglisse@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 mm/huge_memory.c | 14 +++++++++++++-
 mm/migrate.c     | 19 ++++++-------------
 2 files changed, 19 insertions(+), 14 deletions(-)

diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index deed97fba979..a71a5172104c 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -1562,8 +1562,20 @@ vm_fault_t do_huge_pmd_numa_page(struct vm_fault *vmf, pmd_t pmd)
 	 * We are not sure a pending tlb flush here is for a huge page
 	 * mapping or not. Hence use the tlb range variant
 	 */
-	if (mm_tlb_flush_pending(vma->vm_mm))
+	if (mm_tlb_flush_pending(vma->vm_mm)) {
 		flush_tlb_range(vma, haddr, haddr + HPAGE_PMD_SIZE);
+		/*
+		 * change_huge_pmd() released the pmd lock before
+		 * invalidating the secondary MMUs sharing the primary
+		 * MMU pagetables (with ->invalidate_range()). The
+		 * mmu_notifier_invalidate_range_end() (which
+		 * internally calls ->invalidate_range()) in
+		 * change_pmd_range() will run after us, so we can't
+		 * rely on it here and we need an explicit invalidate.
+		 */
+		mmu_notifier_invalidate_range(vma->vm_mm, haddr,
+					      haddr + HPAGE_PMD_SIZE);
+	}
 
 	/*
 	 * Migrate the THP to the requested node, returns with page unlocked
diff --git a/mm/migrate.c b/mm/migrate.c
index 1f634b1563b6..1637a32f3dd7 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -1973,8 +1973,8 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm,
 	int isolated = 0;
 	struct page *new_page = NULL;
 	int page_lru = page_is_file_cache(page);
-	unsigned long mmun_start = address & HPAGE_PMD_MASK;
-	unsigned long mmun_end = mmun_start + HPAGE_PMD_SIZE;
+	unsigned long start = address & HPAGE_PMD_MASK;
+	unsigned long end = start + HPAGE_PMD_SIZE;
 
 	new_page = alloc_pages_node(node,
 		(GFP_TRANSHUGE_LIGHT | __GFP_THISNODE),
@@ -2001,11 +2001,9 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm,
 	WARN_ON(PageLRU(new_page));
 
 	/* Recheck the target PMD */
-	mmu_notifier_invalidate_range_start(mm, mmun_start, mmun_end);
 	ptl = pmd_lock(mm, pmd);
 	if (unlikely(!pmd_same(*pmd, entry) || !page_ref_freeze(page, 2))) {
 		spin_unlock(ptl);
-		mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end);
 
 		/* Reverse changes made by migrate_page_copy() */
 		if (TestClearPageActive(new_page))
@@ -2036,8 +2034,8 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm,
 	 * new page and page_add_new_anon_rmap guarantee the copy is
 	 * visible before the pagetable update.
 	 */
-	flush_cache_range(vma, mmun_start, mmun_end);
-	page_add_anon_rmap(new_page, vma, mmun_start, true);
+	flush_cache_range(vma, start, end);
+	page_add_anon_rmap(new_page, vma, start, true);
 	/*
 	 * At this point the pmd is numa/protnone (i.e. non present) and the TLB
 	 * has already been flushed globally.  So no TLB can be currently
@@ -2049,7 +2047,7 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm,
 	 * MADV_DONTNEED won't wait on the pmd lock and it'll skip clearing this
 	 * pmd.
 	 */
-	set_pmd_at(mm, mmun_start, pmd, entry);
+	set_pmd_at(mm, start, pmd, entry);
 	update_mmu_cache_pmd(vma, address, &entry);
 
 	page_ref_unfreeze(page, 2);
@@ -2058,11 +2056,6 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm,
 	set_page_owner_migrate_reason(new_page, MR_NUMA_MISPLACED);
 
 	spin_unlock(ptl);
-	/*
-	 * No need to double call mmu_notifier->invalidate_range() callback as
-	 * the above pmdp_huge_clear_flush_notify() did already call it.
-	 */
-	mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end);
 
 	/* Take an "isolate" reference and put new page on the LRU. */
 	get_page(new_page);
@@ -2086,7 +2079,7 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm,
 	ptl = pmd_lock(mm, pmd);
 	if (pmd_same(*pmd, entry)) {
 		entry = pmd_modify(entry, vma->vm_page_prot);
-		set_pmd_at(mm, mmun_start, pmd, entry);
+		set_pmd_at(mm, start, pmd, entry);
 		update_mmu_cache_pmd(vma, address, &entry);
 	}
 	spin_unlock(ptl);
-- 
2.17.1


  reply	other threads:[~2018-11-04 13:51 UTC|newest]

Thread overview: 58+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-11-04 13:50 [PATCH AUTOSEL 4.19 01/57] mm: thp: fix MADV_DONTNEED vs migrate_misplaced_transhuge_page race condition Sasha Levin
2018-11-04 13:50 ` Sasha Levin [this message]
2018-11-04 13:50 ` [PATCH AUTOSEL 4.19 03/57] mm: calculate deferred pages after skipping mirrored memory Sasha Levin
2018-11-04 13:50 ` [PATCH AUTOSEL 4.19 04/57] mm: don't raise MEMCG_OOM event due to failed high-order allocation Sasha Levin
2018-11-04 13:50 ` [PATCH AUTOSEL 4.19 05/57] mm/vmstat.c: assert that vmstat_text is in sync with stat_items_size Sasha Levin
2018-11-04 13:50 ` [PATCH AUTOSEL 4.19 06/57] userfaultfd: allow get_mempolicy(MPOL_F_NODE|MPOL_F_ADDR) to trigger userfaults Sasha Levin
2018-11-04 13:50 ` [PATCH AUTOSEL 4.19 07/57] mm: don't miss the last page because of round-off error Sasha Levin
2018-11-04 13:50 ` [PATCH AUTOSEL 4.19 08/57] mm: don't warn about large allocations for slab Sasha Levin
2018-11-04 13:50 ` [PATCH AUTOSEL 4.19 09/57] r8169: fix broken Wake-on-LAN from S5 (poweroff) Sasha Levin
2018-11-04 13:50 ` [PATCH AUTOSEL 4.19 10/57] powerpc/traps: restore recoverability of machine_check interrupts Sasha Levin
2018-11-04 13:50 ` [PATCH AUTOSEL 4.19 11/57] powerpc/64/module: REL32 relocation range check Sasha Levin
2018-11-04 13:50 ` [PATCH AUTOSEL 4.19 12/57] powerpc/mm: Fix page table dump to work on Radix Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 13/57] powerpc/mm: fix always true/false warning in slice.c Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 14/57] drm/amd/display: fix bug of accessing invalid memory Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 15/57] Input: wm97xx-ts - fix exit path Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 16/57] powerpc/Makefile: Fix PPC_BOOK3S_64 ASFLAGS Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 17/57] powerpc/eeh: Fix possible null deref in eeh_dump_dev_log() Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 18/57] tty: check name length in tty_find_polling_driver() Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 19/57] tracing/kprobes: Check the probe on unloaded module correctly Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 20/57] drm/nouveau/secboot/acr: fix memory leak Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 21/57] drm/amdgpu/powerplay: fix missing break in switch statements Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 22/57] ARM: imx_v6_v7_defconfig: Select CONFIG_TMPFS_POSIX_ACL Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 23/57] powerpc/nohash: fix undefined behaviour when testing page size support Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 24/57] drm/msm/gpu: fix parameters in function msm_gpu_crashstate_capture Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 25/57] drm/msm/disp/dpu: Use proper define for drm_encoder_init() 'encoder_type' Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 26/57] drm/msm: dpu: Allow planes to extend past active display Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 27/57] powerpc/mm: Don't report hugepage tables as memory leaks when using kmemleak Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 28/57] watchdog: lantiq: update register names to better match spec Sasha Levin
2018-11-05 22:26   ` Hauke Mehrtens
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 29/57] drm/omap: fix memory barrier bug in DMM driver Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 30/57] iio: adc: at91: fix wrong channel number in triggered buffer mode Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 31/57] iio: adc: at91: fix acking DRDY irq on simple conversions Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 32/57] drm/amd/display: Raise dispclk value for dce120 by 15% Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 33/57] drm/amd/display: fix gamma not being applied Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 34/57] drm/hisilicon: hibmc: Do not carry error code in HiBMC framebuffer pointer Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 35/57] media: pci: cx23885: handle adding to list failure Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 36/57] media: coda: don't overwrite h.264 profile_idc on decoder instance Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 37/57] iio: adc: imx25-gcq: Fix leak of device_node in mx25_gcq_setup_cfgs() Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 38/57] MIPS: kexec: Mark CPU offline before disabling local IRQ Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 39/57] powerpc/boot: Ensure _zimage_start is a weak symbol Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 40/57] powerpc/memtrace: Remove memory in chunks Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 41/57] MIPS/PCI: Call pcie_bus_configure_settings() to set MPS/MRRS Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 42/57] staging: erofs: fix a missing endian conversion Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 43/57] serial: 8250_of: Fix for lack of interrupt support Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 44/57] sc16is7xx: Fix for multi-channel stall Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 45/57] media: tvp5150: fix width alignment during set_selection() Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 46/57] powerpc/selftests: Wait all threads to join Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 47/57] staging:iio:ad7606: fix voltage scales Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 48/57] drm: rcar-du: Update Gen3 output limitations Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 49/57] drm/amdgpu: Fix SDMA TO after GPU reset v3 Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 50/57] staging: most: video: fix registration of an empty comp core_component Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 51/57] 9p locks: fix glock.client_id leak in do_lock Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 52/57] udf: Prevent write-unsupported filesystem to be remounted read-write Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 53/57] ARM: dts: imx6ull: keep IMX6UL_ prefix for signals on both i.MX6UL and i.MX6ULL Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 54/57] media: ov5640: fix mode change regression Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 55/57] 9p: clear dangling pointers in p9stat_free Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 56/57] drm/amdgpu: fix integer overflow test in amdgpu_bo_list_create() Sasha Levin
2018-11-04 13:51 ` [PATCH AUTOSEL 4.19 57/57] media: ov5640: fix restore of last mode set Sasha Levin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20181104135144.88324-2-sashal@kernel.org \
    --to=sashal@kernel.org \
    --cc=aarcange@redhat.com \
    --cc=akpm@linux-foundation.org \
    --cc=jglisse@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=stable@vger.kernel.org \
    --cc=torvalds@linux-foundation.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.