diff options
author | Toshi Kani <toshi.kani@hpe.com> | 2015-12-22 19:54:23 -0500 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2016-01-05 05:10:05 -0500 |
commit | d9fe4fab11976e56b2e992980bf6ce948bdf02ac (patch) | |
tree | efbf8627823ee26cbbc397d8aa7ba158a849eb31 | |
parent | 9abb0ecdee69a2577560cc283368e490da974934 (diff) |
x86/mm/pat: Add untrack_pfn_moved for mremap
mremap() with MREMAP_FIXED on a VM_PFNMAP range causes the following
WARN_ON_ONCE() message in untrack_pfn().
WARNING: CPU: 1 PID: 3493 at arch/x86/mm/pat.c:985 untrack_pfn+0xbd/0xd0()
Call Trace:
[<ffffffff817729ea>] dump_stack+0x45/0x57
[<ffffffff8109e4b6>] warn_slowpath_common+0x86/0xc0
[<ffffffff8109e5ea>] warn_slowpath_null+0x1a/0x20
[<ffffffff8106a88d>] untrack_pfn+0xbd/0xd0
[<ffffffff811d2d5e>] unmap_single_vma+0x80e/0x860
[<ffffffff811d3725>] unmap_vmas+0x55/0xb0
[<ffffffff811d916c>] unmap_region+0xac/0x120
[<ffffffff811db86a>] do_munmap+0x28a/0x460
[<ffffffff811dec33>] move_vma+0x1b3/0x2e0
[<ffffffff811df113>] SyS_mremap+0x3b3/0x510
[<ffffffff817793ee>] entry_SYSCALL_64_fastpath+0x12/0x71
MREMAP_FIXED moves a pfnmap from old vma to new vma. untrack_pfn() is
called with the old vma after its pfnmap page table has been removed,
which causes follow_phys() to fail. The new vma has a new pfnmap to
the same pfn & cache type with VM_PAT set. Therefore, we only need to
clear VM_PAT from the old vma in this case.
Add untrack_pfn_moved(), which clears VM_PAT from a given old vma.
move_vma() is changed to call this function with the old vma when
VM_PFNMAP is set. move_vma() then calls do_munmap(), and untrack_pfn()
is a no-op since VM_PAT is cleared.
Reported-by: Stas Sergeev <stsp@list.ru>
Signed-off-by: Toshi Kani <toshi.kani@hpe.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Borislav Petkov <bp@suse.de>
Cc: linux-mm@kvack.org
Link: http://lkml.kernel.org/r/1450832064-10093-2-git-send-email-toshi.kani@hpe.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | arch/x86/mm/pat.c | 10 | ||||
-rw-r--r-- | include/asm-generic/pgtable.h | 10 | ||||
-rw-r--r-- | mm/mremap.c | 4 |
3 files changed, 23 insertions, 1 deletions
diff --git a/arch/x86/mm/pat.c b/arch/x86/mm/pat.c index 188e3e07eeeb..1aca073ba571 100644 --- a/arch/x86/mm/pat.c +++ b/arch/x86/mm/pat.c | |||
@@ -992,6 +992,16 @@ void untrack_pfn(struct vm_area_struct *vma, unsigned long pfn, | |||
992 | vma->vm_flags &= ~VM_PAT; | 992 | vma->vm_flags &= ~VM_PAT; |
993 | } | 993 | } |
994 | 994 | ||
995 | /* | ||
996 | * untrack_pfn_moved is called, while mremapping a pfnmap for a new region, | ||
997 | * with the old vma after its pfnmap page table has been removed. The new | ||
998 | * vma has a new pfnmap to the same pfn & cache type with VM_PAT set. | ||
999 | */ | ||
1000 | void untrack_pfn_moved(struct vm_area_struct *vma) | ||
1001 | { | ||
1002 | vma->vm_flags &= ~VM_PAT; | ||
1003 | } | ||
1004 | |||
995 | pgprot_t pgprot_writecombine(pgprot_t prot) | 1005 | pgprot_t pgprot_writecombine(pgprot_t prot) |
996 | { | 1006 | { |
997 | return __pgprot(pgprot_val(prot) | | 1007 | return __pgprot(pgprot_val(prot) | |
diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h index 14b0ff32fb9f..3a6803cb0ec9 100644 --- a/include/asm-generic/pgtable.h +++ b/include/asm-generic/pgtable.h | |||
@@ -569,7 +569,7 @@ static inline int track_pfn_copy(struct vm_area_struct *vma) | |||
569 | } | 569 | } |
570 | 570 | ||
571 | /* | 571 | /* |
572 | * untrack_pfn_vma is called while unmapping a pfnmap for a region. | 572 | * untrack_pfn is called while unmapping a pfnmap for a region. |
573 | * untrack can be called for a specific region indicated by pfn and size or | 573 | * untrack can be called for a specific region indicated by pfn and size or |
574 | * can be for the entire vma (in which case pfn, size are zero). | 574 | * can be for the entire vma (in which case pfn, size are zero). |
575 | */ | 575 | */ |
@@ -577,6 +577,13 @@ static inline void untrack_pfn(struct vm_area_struct *vma, | |||
577 | unsigned long pfn, unsigned long size) | 577 | unsigned long pfn, unsigned long size) |
578 | { | 578 | { |
579 | } | 579 | } |
580 | |||
581 | /* | ||
582 | * untrack_pfn_moved is called while mremapping a pfnmap for a new region. | ||
583 | */ | ||
584 | static inline void untrack_pfn_moved(struct vm_area_struct *vma) | ||
585 | { | ||
586 | } | ||
580 | #else | 587 | #else |
581 | extern int track_pfn_remap(struct vm_area_struct *vma, pgprot_t *prot, | 588 | extern int track_pfn_remap(struct vm_area_struct *vma, pgprot_t *prot, |
582 | unsigned long pfn, unsigned long addr, | 589 | unsigned long pfn, unsigned long addr, |
@@ -586,6 +593,7 @@ extern int track_pfn_insert(struct vm_area_struct *vma, pgprot_t *prot, | |||
586 | extern int track_pfn_copy(struct vm_area_struct *vma); | 593 | extern int track_pfn_copy(struct vm_area_struct *vma); |
587 | extern void untrack_pfn(struct vm_area_struct *vma, unsigned long pfn, | 594 | extern void untrack_pfn(struct vm_area_struct *vma, unsigned long pfn, |
588 | unsigned long size); | 595 | unsigned long size); |
596 | extern void untrack_pfn_moved(struct vm_area_struct *vma); | ||
589 | #endif | 597 | #endif |
590 | 598 | ||
591 | #ifdef __HAVE_COLOR_ZERO_PAGE | 599 | #ifdef __HAVE_COLOR_ZERO_PAGE |
diff --git a/mm/mremap.c b/mm/mremap.c index c25bc6268e46..de824e72c3e8 100644 --- a/mm/mremap.c +++ b/mm/mremap.c | |||
@@ -319,6 +319,10 @@ static unsigned long move_vma(struct vm_area_struct *vma, | |||
319 | hiwater_vm = mm->hiwater_vm; | 319 | hiwater_vm = mm->hiwater_vm; |
320 | vm_stat_account(mm, vma->vm_flags, vma->vm_file, new_len>>PAGE_SHIFT); | 320 | vm_stat_account(mm, vma->vm_flags, vma->vm_file, new_len>>PAGE_SHIFT); |
321 | 321 | ||
322 | /* Tell pfnmap has moved from this vma */ | ||
323 | if (unlikely(vma->vm_flags & VM_PFNMAP)) | ||
324 | untrack_pfn_moved(vma); | ||
325 | |||
322 | if (do_munmap(mm, old_addr, old_len) < 0) { | 326 | if (do_munmap(mm, old_addr, old_len) < 0) { |
323 | /* OOM: unable to split vma, just get accounts right */ | 327 | /* OOM: unable to split vma, just get accounts right */ |
324 | vm_unacct_memory(excess >> PAGE_SHIFT); | 328 | vm_unacct_memory(excess >> PAGE_SHIFT); |