Commit 64141da5 authored by Jeremy Fitzhardinge's avatar Jeremy Fitzhardinge Committed by Linus Torvalds

vmalloc: eagerly clear ptes on vunmap

On stock 2.6.37-rc4, running:

  # mount lilith:/export /mnt/lilith
  # find  /mnt/lilith/ -type f -print0 | xargs -0 file

crashes the machine fairly quickly under Xen.  Often it results in oops
messages, but the couple of times I tried just now, it just hung quietly
and made Xen print some rude messages:

    (XEN) mm.c:2389:d80 Bad type (saw 7400000000000001 != exp
    3000000000000000) for mfn 1d7058 (pfn 18fa7)
    (XEN) mm.c:964:d80 Attempt to create linear p.t. with write perms
    (XEN) mm.c:2389:d80 Bad type (saw 7400000000000010 != exp
    1000000000000000) for mfn 1d2e04 (pfn 1d1fb)
    (XEN) mm.c:2965:d80 Error while pinning mfn 1d2e04

Which means the domain tried to map a pagetable page RW, which would
allow it to map arbitrary memory, so Xen stopped it.  This is because
vm_unmap_ram() left some pages mapped in the vmalloc area after NFS had
finished with them, and those pages got recycled as pagetable pages
while still having these RW aliases.

Removing those mappings immediately removes the Xen-visible aliases, and
so it has no problem with those pages being reused as pagetable pages.
Deferring the TLB flush doesn't upset Xen because it can flush the TLB
itself as needed to maintain its invariants.

When unmapping a region in the vmalloc space, clear the ptes
immediately.  There's no point in deferring this because there's no
amortization benefit.

The TLBs are left dirty, and they are flushed lazily to amortize the
cost of the IPIs.

This specific motivation for this patch is an oops-causing regression
since 2.6.36 when using NFS under Xen, triggered by the NFS client's use
of vm_map_ram() introduced in 56e4ebf8 ("NFS: readdir with vmapped
pages") .  XFS also uses vm_map_ram() and could cause similar problems.
Signed-off-by: default avatarJeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
Cc: Nick Piggin <npiggin@kernel.dk>
Cc: Bryan Schumaker <bjschuma@netapp.com>
Cc: Trond Myklebust <Trond.Myklebust@netapp.com>
Cc: Alex Elder <aelder@sgi.com>
Cc: Dave Chinner <david@fromorbit.com>
Cc: Christoph Hellwig <hch@lst.de>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 853ff883
...@@ -2415,8 +2415,6 @@ void __init xen_init_mmu_ops(void) ...@@ -2415,8 +2415,6 @@ void __init xen_init_mmu_ops(void)
x86_init.paging.pagetable_setup_done = xen_pagetable_setup_done; x86_init.paging.pagetable_setup_done = xen_pagetable_setup_done;
pv_mmu_ops = xen_mmu_ops; pv_mmu_ops = xen_mmu_ops;
vmap_lazy_unmap = false;
memset(dummy_mapping, 0xff, PAGE_SIZE); memset(dummy_mapping, 0xff, PAGE_SIZE);
} }
......
...@@ -7,8 +7,6 @@ ...@@ -7,8 +7,6 @@
struct vm_area_struct; /* vma defining user mapping in mm_types.h */ struct vm_area_struct; /* vma defining user mapping in mm_types.h */
extern bool vmap_lazy_unmap;
/* bits in flags of vmalloc's vm_struct below */ /* bits in flags of vmalloc's vm_struct below */
#define VM_IOREMAP 0x00000001 /* ioremap() and friends */ #define VM_IOREMAP 0x00000001 /* ioremap() and friends */
#define VM_ALLOC 0x00000002 /* vmalloc() */ #define VM_ALLOC 0x00000002 /* vmalloc() */
......
...@@ -31,8 +31,6 @@ ...@@ -31,8 +31,6 @@
#include <asm/tlbflush.h> #include <asm/tlbflush.h>
#include <asm/shmparam.h> #include <asm/shmparam.h>
bool vmap_lazy_unmap __read_mostly = true;
/*** Page table manipulation functions ***/ /*** Page table manipulation functions ***/
static void vunmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end) static void vunmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end)
...@@ -503,9 +501,6 @@ static unsigned long lazy_max_pages(void) ...@@ -503,9 +501,6 @@ static unsigned long lazy_max_pages(void)
{ {
unsigned int log; unsigned int log;
if (!vmap_lazy_unmap)
return 0;
log = fls(num_online_cpus()); log = fls(num_online_cpus());
return log * (32UL * 1024 * 1024 / PAGE_SIZE); return log * (32UL * 1024 * 1024 / PAGE_SIZE);
...@@ -566,7 +561,6 @@ static void __purge_vmap_area_lazy(unsigned long *start, unsigned long *end, ...@@ -566,7 +561,6 @@ static void __purge_vmap_area_lazy(unsigned long *start, unsigned long *end,
if (va->va_end > *end) if (va->va_end > *end)
*end = va->va_end; *end = va->va_end;
nr += (va->va_end - va->va_start) >> PAGE_SHIFT; nr += (va->va_end - va->va_start) >> PAGE_SHIFT;
unmap_vmap_area(va);
list_add_tail(&va->purge_list, &valist); list_add_tail(&va->purge_list, &valist);
va->flags |= VM_LAZY_FREEING; va->flags |= VM_LAZY_FREEING;
va->flags &= ~VM_LAZY_FREE; va->flags &= ~VM_LAZY_FREE;
...@@ -611,10 +605,11 @@ static void purge_vmap_area_lazy(void) ...@@ -611,10 +605,11 @@ static void purge_vmap_area_lazy(void)
} }
/* /*
* Free and unmap a vmap area, caller ensuring flush_cache_vunmap had been * Free a vmap area, caller ensuring that the area has been unmapped
* called for the correct range previously. * and flush_cache_vunmap had been called for the correct range
* previously.
*/ */
static void free_unmap_vmap_area_noflush(struct vmap_area *va) static void free_vmap_area_noflush(struct vmap_area *va)
{ {
va->flags |= VM_LAZY_FREE; va->flags |= VM_LAZY_FREE;
atomic_add((va->va_end - va->va_start) >> PAGE_SHIFT, &vmap_lazy_nr); atomic_add((va->va_end - va->va_start) >> PAGE_SHIFT, &vmap_lazy_nr);
...@@ -622,6 +617,16 @@ static void free_unmap_vmap_area_noflush(struct vmap_area *va) ...@@ -622,6 +617,16 @@ static void free_unmap_vmap_area_noflush(struct vmap_area *va)
try_purge_vmap_area_lazy(); try_purge_vmap_area_lazy();
} }
/*
* Free and unmap a vmap area, caller ensuring flush_cache_vunmap had been
* called for the correct range previously.
*/
static void free_unmap_vmap_area_noflush(struct vmap_area *va)
{
unmap_vmap_area(va);
free_vmap_area_noflush(va);
}
/* /*
* Free and unmap a vmap area * Free and unmap a vmap area
*/ */
...@@ -798,7 +803,7 @@ static void free_vmap_block(struct vmap_block *vb) ...@@ -798,7 +803,7 @@ static void free_vmap_block(struct vmap_block *vb)
spin_unlock(&vmap_block_tree_lock); spin_unlock(&vmap_block_tree_lock);
BUG_ON(tmp != vb); BUG_ON(tmp != vb);
free_unmap_vmap_area_noflush(vb->va); free_vmap_area_noflush(vb->va);
call_rcu(&vb->rcu_head, rcu_free_vb); call_rcu(&vb->rcu_head, rcu_free_vb);
} }
...@@ -936,6 +941,8 @@ static void vb_free(const void *addr, unsigned long size) ...@@ -936,6 +941,8 @@ static void vb_free(const void *addr, unsigned long size)
rcu_read_unlock(); rcu_read_unlock();
BUG_ON(!vb); BUG_ON(!vb);
vunmap_page_range((unsigned long)addr, (unsigned long)addr + size);
spin_lock(&vb->lock); spin_lock(&vb->lock);
BUG_ON(bitmap_allocate_region(vb->dirty_map, offset >> PAGE_SHIFT, order)); BUG_ON(bitmap_allocate_region(vb->dirty_map, offset >> PAGE_SHIFT, order));
...@@ -988,7 +995,6 @@ void vm_unmap_aliases(void) ...@@ -988,7 +995,6 @@ void vm_unmap_aliases(void)
s = vb->va->va_start + (i << PAGE_SHIFT); s = vb->va->va_start + (i << PAGE_SHIFT);
e = vb->va->va_start + (j << PAGE_SHIFT); e = vb->va->va_start + (j << PAGE_SHIFT);
vunmap_page_range(s, e);
flush = 1; flush = 1;
if (s < start) if (s < start)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment