mremap() with MREMAP_FIXED, remapping to a new virtual address, 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 virtual address of VM_PFNMAP, but keeps the pfn and cache type. In this case, untrack_pfn() is called with the old vma after its translation has removed. Hence, when follow_phys() fails, track_pfn() is changed to keep the pfn tracked and clears VM_PAT from the old vma, instead of WARN_ON_ONCE() on the case. Reference: https://lkml.org/lkml/2015/10/28/865 Reported-by: Stas Sergeev <stsp@xxxxxxx> Signed-off-by: Toshi Kani <toshi.kani@xxxxxxx> Cc: Thomas Gleixner <tglx@xxxxxxxxxxxxx> Cc: Ingo Molnar <mingo@xxxxxxxxxx> Cc: H. Peter Anvin <hpa@xxxxxxxxx> Cc: Borislav Petkov <bp@xxxxxxx> --- arch/x86/mm/pat.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/arch/x86/mm/pat.c b/arch/x86/mm/pat.c index 188e3e0..f3e391e 100644 --- a/arch/x86/mm/pat.c +++ b/arch/x86/mm/pat.c @@ -966,8 +966,14 @@ int track_pfn_insert(struct vm_area_struct *vma, pgprot_t *prot, /* * untrack_pfn is called while unmapping a pfnmap for a region. - * untrack can be called for a specific region indicated by pfn and size or - * can be for the entire vma (in which case pfn, size are zero). + * untrack_pfn can be called for a specific region indicated by pfn and + * size or can be for the entire vma (in which case pfn, size are zero). + * + * NOTE: mremap may move a virtual address of VM_PFNMAP, but keeps the + * pfn and cache type. In this case, untrack_pfn() is called with the + * old vma after its translation has removed. Hence, when follow_phys() + * fails, track_pfn() keeps the pfn tracked and clears VM_PAT from the + * old vma. */ void untrack_pfn(struct vm_area_struct *vma, unsigned long pfn, unsigned long size) @@ -981,14 +987,13 @@ void untrack_pfn(struct vm_area_struct *vma, unsigned long pfn, /* free the chunk starting from pfn or the whole chunk */ paddr = (resource_size_t)pfn << PAGE_SHIFT; if (!paddr && !size) { - if (follow_phys(vma, vma->vm_start, 0, &prot, &paddr)) { - WARN_ON_ONCE(1); - return; - } + if (follow_phys(vma, vma->vm_start, 0, &prot, &paddr)) + goto out; size = vma->vm_end - vma->vm_start; } free_pfn_range(paddr, size); +out: vma->vm_flags &= ~VM_PAT; } -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@xxxxxxxxx. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"dont@xxxxxxxxx"> email@xxxxxxxxx </a>